mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 03:59:19 +02:00
Add vehicle graph core: .vhg loader, logic and cab blocks, -graph-run and -graph-catalog
This commit is contained in:
@@ -57,7 +57,9 @@ file(GLOB HEADERS "*.h"
|
||||
"model/*.h"
|
||||
"utilities/*.h"
|
||||
"world/*.h"
|
||||
"vehicle/*.h")
|
||||
"vehicle/*.h"
|
||||
"vehicle/graph/*.h"
|
||||
"vehicle/graph/blocks/*.h")
|
||||
|
||||
set(GLOBAL_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(GLOBAL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/global_include")
|
||||
@@ -109,6 +111,15 @@ set(SOURCES
|
||||
"vehicle/Driver.cpp"
|
||||
"application/driverhints.cpp"
|
||||
"vehicle/DynObj.cpp"
|
||||
"vehicle/graph/graph_types.cpp"
|
||||
"vehicle/graph/graph_block.cpp"
|
||||
"vehicle/graph/graph_catalog.cpp"
|
||||
"vehicle/graph/graph_format.cpp"
|
||||
"vehicle/graph/graph_template.cpp"
|
||||
"vehicle/graph/graph_instance.cpp"
|
||||
"vehicle/graph/graph_standalone.cpp"
|
||||
"vehicle/graph/blocks/logic.cpp"
|
||||
"vehicle/graph/blocks/cab.cpp"
|
||||
"EU07.cpp"
|
||||
"export_e3d_standalone.cpp"
|
||||
"export_btf_standalone.cpp"
|
||||
|
||||
17
EU07.cpp
17
EU07.cpp
@@ -20,6 +20,7 @@ Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others
|
||||
|
||||
#include "application/application.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "vehicle/graph/graph_standalone.h"
|
||||
#include <cstdlib>
|
||||
#ifdef WITHDUMPGEN
|
||||
#ifdef _WIN32
|
||||
@@ -130,6 +131,22 @@ int main(int argc, char *argv[])
|
||||
fflush(stderr);
|
||||
std::_Exit(result);
|
||||
}
|
||||
// standalone check and run of a vehicle graph: -graph-run <file.vhg> [-script <events>] [-o <results.csv>] [-dt <step>] [-t <duration>] [-every <steps>]
|
||||
else if (argc >= 2 && std::string(argv[1]) == "-graph-run")
|
||||
{
|
||||
auto const result{vgraph::graph_run_standalone(argc, argv)};
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
std::_Exit(result);
|
||||
}
|
||||
// export of the vehicle graph block catalog for the external editor: -graph-catalog [-o <file.json>]
|
||||
else if (argc >= 2 && std::string(argv[1]) == "-graph-catalog")
|
||||
{
|
||||
auto const result{vgraph::graph_catalog_standalone(argc >= 4 && std::string(argv[2]) == "-o" ? argv[3] : "")};
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
std::_Exit(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
|
||||
107
tools/vehicle_graph/README.md
Normal file
107
tools/vehicle_graph/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Vehicle graph (.vhg)
|
||||
|
||||
A vehicle graph describes a vehicle as connected blocks, in place of the `.fiz` file. The graph holds the cab
|
||||
controls and indicators, the logic, the sounds and, in the following stages, the electrical, mechanical and
|
||||
pneumatic systems of the vehicle. The simulator code is in `vehicle/graph/`.
|
||||
|
||||
Current stage: the core of the graph with logic and cab blocks, available through the command line tools below.
|
||||
Vehicles aren't loaded from `.vhg` files yet.
|
||||
|
||||
## File format, version 2
|
||||
|
||||
A `.vhg` file is JSON. Comments (`//`, `/* */`) are allowed.
|
||||
|
||||
```json
|
||||
{
|
||||
"format": "maszyna-vehicle-graph",
|
||||
"version": 2,
|
||||
"vehicle": { "name": "ET22" },
|
||||
"settings": { "low_voltage_simulation": false },
|
||||
"body": { },
|
||||
"profiles": { "brake_pipe": 1 },
|
||||
"nodes": [
|
||||
{ "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw:" } },
|
||||
{ "id": "lamp", "type": "fb.lamp", "params": { "color": "red" } }
|
||||
],
|
||||
"connections": [
|
||||
{ "from": "bat.pos", "to": "lamp.on" }
|
||||
],
|
||||
"layout": { }
|
||||
}
|
||||
```
|
||||
|
||||
- `nodes[].id`: letters, digits, `_` and `-`; unique in the file.
|
||||
- `nodes[].type`: block type from the catalog (see `-graph-catalog`).
|
||||
- `nodes[].params`: typed values. Numbers are JSON numbers, switches are `true`/`false`, choices are ASCII ids
|
||||
from the catalog (e.g. `"bistable"`, `"rotate"`, `"ne"`). Parameters left out take their default values.
|
||||
- `connections`: `"node.port"` of an output to `"node.port"` of an input. An output can feed many inputs; an input
|
||||
has at most one source. Unconnected inputs see their default value.
|
||||
- `settings.low_voltage_simulation`: when enabled, blocks with low voltage ports simulate the voltages and currents
|
||||
of the control circuits (e.g. a lamp needs enough voltage and draws current). When disabled, low voltage
|
||||
connections have no effect and the apparatus behaves as if always powered.
|
||||
- `body`, `profiles`: parameters of the vehicle body and standard interfaces between vehicles; used by the next stages.
|
||||
- `layout`: positions of the blocks in the editor; ignored by the simulator.
|
||||
|
||||
### Ports and domains
|
||||
|
||||
Signal ports carry one value (bool, int or float; booleans are 0 and 1). Physical ports carry a pair of values
|
||||
in opposite directions:
|
||||
|
||||
| domain | forward | back |
|
||||
|---|---|---|
|
||||
| `high_voltage`, `low_voltage` | voltage [V] | current [A] |
|
||||
| `aux_ac` | voltage [V] | power [W] |
|
||||
| `drive` | demand [0-1] | power [W] |
|
||||
| `mechanical` | torque [Nm] | rotational speed [rad/s] |
|
||||
| `traction` | force at the wheel rim [N] | velocity [m/s] |
|
||||
| `air`, `brake_pipe`, `feed_pipe` | pressure [bar] | flow |
|
||||
|
||||
### Order of evaluation
|
||||
|
||||
Blocks are evaluated once per step in the order given by the connections. A loop of connections has to pass through
|
||||
a deferred block (`logic.sr`, `logic.ton`, `logic.tof`, `logic.delay`), whose outputs change in the next step; other
|
||||
loops are rejected when the file is loaded. Back values of physical connections are always taken from the previous
|
||||
step. The graph has no other source of time or randomness, so the same inputs give the same results.
|
||||
|
||||
## Command line tools
|
||||
|
||||
```
|
||||
eu07 -graph-catalog [-o <catalog.json>]
|
||||
eu07 -graph-run <file.vhg> [-script <events>] [-o <results.csv>] [-dt <step, s>] [-t <duration, s>] [-every <steps>]
|
||||
```
|
||||
|
||||
`-graph-catalog` writes the description of all block types, ports and parameters, for the external editor.
|
||||
|
||||
`-graph-run` loads and checks the graph, reports problems, and runs it without the simulator. The results are
|
||||
the values of all signal outputs and the states of the blocks (e.g. `lamp:lit`), separated with `;`.
|
||||
The script lists events, one per line:
|
||||
|
||||
```
|
||||
# <time, s> <operation> <target> [value]
|
||||
0.0 toggle bat
|
||||
6.0 press ws_on
|
||||
6.3 release ws_on
|
||||
11.0 inc reverser
|
||||
21.0 set lever 10
|
||||
1.1 force x.q 3
|
||||
1.7 unforce x.q
|
||||
```
|
||||
|
||||
Operations on cab switches: `press`, `release`, `toggle`, `inc`, `dec`, `set <position>`, `value <value>`.
|
||||
Operations on signal ports: `force <node.port> <value>`, `unforce <node.port>`.
|
||||
|
||||
On Windows the simulator runs without a console, so use `-o` for the results, and redirect the output to a file
|
||||
to see the messages, e.g. `eu07.exe -graph-run test.vhg > log.txt`.
|
||||
|
||||
## Tests
|
||||
|
||||
```
|
||||
python3 tools/vehicle_graph/run_tests.py <path to the simulator executable> [--update]
|
||||
```
|
||||
|
||||
- `tests/*.vhg` start with `// expect: ok`, `// expect: error <text>` or `// expect: warning <text>`;
|
||||
- every `.vhg` with a `.script` is run twice; both runs have to give identical results, equal to `.expected.csv`.
|
||||
`--update` replaces the expected results with the current ones, after checking the change.
|
||||
|
||||
`examples/` holds the cab logic of two vehicles, modelled after the 303E (ET22, resistor starting) and Vectron
|
||||
(induction motors) `.fiz` files.
|
||||
181
tools/vehicle_graph/examples/303e-logic.expected.csv
Normal file
181
tools/vehicle_graph/examples/303e-logic.expected.csv
Normal file
@@ -0,0 +1,181 @@
|
||||
time;bat.pos;bat.index;pant_f.pos;pant_f.index;pant_r.pos;pant_r.index;ws_on.pos;ws_on.index;ws_off.pos;ws_off.index;conv_sw.pos;conv_sw.index;comp_sw.pos;comp_sw.index;reverser.pos;reverser.index;mainctrl.pos;mainctrl.index;pant_any.q;pant_air.q;pant_up.q;hv_nominal.q;hv.q;hv_ok.q;ws_close.q;ws_close2.q;ws_lost.q;ws_open.q;ws.q;ws_lamp_on.q;conv_run.q;conv.q;conv_lamp_on.q;comp.q;dir_set.q;power_req.q;line_req.q;line.q;line_lamp_on.q;bat:position;pant_f:position;pant_r:position;ws_on:position;ws_off:position;conv_sw:position;comp_sw:position;reverser:position;mainctrl:position;pant_up:elapsed;ws:state;conv:elapsed;lamp_ws:lit;lamp_conv:lit;lamp_line:lit;v_hv:value;snd_conv:playing;snd_conv:volume;snd_conv:pitch;snd_comp:playing;snd_comp:volume;snd_comp:pitch
|
||||
0.1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;0;0;0;0;0;0;1;0;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.2;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;0;0;0;0;0;0;1;0;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.3;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;0;0;0;0;0;0;1;0;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.4;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;0;0;0;0;0;0;1;0;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.5;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;1;0;0;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;0;0;0;0;0;0;1;0;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.1;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.2;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.3;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
0.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.4;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.5;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.6;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.7;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.8;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;0.9;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.1;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.2;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.3;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
1.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.4;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.5;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.6;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.7;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.8;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;1.9;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.1;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.2;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.3;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
2.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.4;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.5;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.6;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.7;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.8;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;2.9;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.1;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.2;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.3;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
3.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.4;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.5;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.6;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.7;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.8;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;3.9;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
4.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
4.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
4.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
4.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.2;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.3;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
5.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
6.1;1;1;1;1;0;0;1;1;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;1;1;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;1;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.2;1;1;1;1;0;0;1;1;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;1;1;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;1;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.3;1;1;1;1;0;0;1;1;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;1;1;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;1;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.4;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.5;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.6;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.8;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
6.9;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
7;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;0;0;1;0;0;0;0;0;1;1;1;0;0;0;0;0;1;0;4;1;0;0;1;1;3000;0;1;1;0;1;1
|
||||
7.1;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.1;0;1;1;3000;0;1;1;0;1;1
|
||||
7.2;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.2;0;1;1;3000;0;1;1;0;1;1
|
||||
7.3;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.3;0;1;1;3000;0;1;1;0;1;1
|
||||
7.4;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.4;0;1;1;3000;0;1;1;0;1;1
|
||||
7.5;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.5;0;1;1;3000;0;1;1;0;1;1
|
||||
7.6;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.6;0;1;1;3000;0;1;1;0;1;1
|
||||
7.7;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.7;0;1;1;3000;0;1;1;0;1;1
|
||||
7.8;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.8;0;1;1;3000;0;1;1;0;1;1
|
||||
7.9;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;0.9;0;1;1;3000;0;1;1;0;1;1
|
||||
8;1;1;1;1;0;0;0;0;0;0;1;1;0;0;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;0;1;0;4;1;1;0;1;1;3000;0;1;1;0;1;1
|
||||
8.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.1;0;1;1;3000;0;1;1;0;1;1
|
||||
8.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.2;0;1;1;3000;0;1;1;0;1;1
|
||||
8.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.3;0;1;1;3000;0;1;1;0;1;1
|
||||
8.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.4;0;1;1;3000;0;1;1;0;1;1
|
||||
8.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.5;0;1;1;3000;0;1;1;0;1;1
|
||||
8.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.6;0;1;1;3000;0;1;1;0;1;1
|
||||
8.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.7;0;1;1;3000;0;1;1;0;1;1
|
||||
8.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.8;0;1;1;3000;0;1;1;0;1;1
|
||||
8.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;1.9;0;1;1;3000;0;1;1;0;1;1
|
||||
9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2;0;1;1;3000;0;1;1;0;1;1
|
||||
9.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.1;0;1;1;3000;0;1;1;0;1;1
|
||||
9.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.2;0;1;1;3000;0;1;1;0;1;1
|
||||
9.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.3;0;1;1;3000;0;1;1;0;1;1
|
||||
9.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.4;0;1;1;3000;0;1;1;0;1;1
|
||||
9.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.5;0;1;1;3000;0;1;1;0;1;1
|
||||
9.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.6;0;1;1;3000;0;1;1;0;1;1
|
||||
9.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.7;0;1;1;3000;0;1;1;0;1;1
|
||||
9.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.8;0;1;1;3000;0;1;1;0;1;1
|
||||
9.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;2.9;0;1;1;3000;0;1;1;0;1;1
|
||||
10;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;0;1;0;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;1;1;3000;0;1;1;0;1;1
|
||||
10.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
10.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11;1;1;1;1;0;0;0;0;0;0;1;1;1;1;0;1;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;0;0;0;0;1;1;1;0;0;0;1;1;1;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;0;0;0;1;1;1;0;0;0;1;1;2;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;0;0;0;1;1;1;0;0;0;1;1;2;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;0;0;0;1;1;1;0;0;0;1;1;2;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;0;0;0;1;1;1;0;0;0;1;1;2;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;0;0;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;0;0;0;1;1;1;0;0;0;1;1;2;0;4;1;3;0;0;1;3000;1;1;1;1;1;1
|
||||
11.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;1;1;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;1;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
11.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
11.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
11.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
12.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.1;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.2;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
13.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
14;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;1;0;1;1;0;1;1;1;1;1;0;1;1;0;0;0;1;1;2;2;4;1;3;0;0;0;3000;1;1;1;1;1;1
|
||||
14.1;1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;1;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;1;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.2;1;1;1;1;0;0;0;0;1;1;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;1;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;1;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.3;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.4;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.5;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.6;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.7;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.8;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
14.9;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
15;1;1;1;1;0;0;0;0;0;0;1;1;1;1;1;2;2;2;1;1;1;3000;3000;1;0;0;0;0;0;1;0;0;1;0;1;1;1;0;1;1;1;0;0;0;1;1;2;2;4;0;0;1;1;1;3000;0;1;1;0;1;1
|
||||
15.1;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.2;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.3;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.4;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.5;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.6;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.7;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.8;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
15.9;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.1;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.2;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.3;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.4;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.5;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.6;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.7;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.8;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
16.9;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17;1;1;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;1;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.1;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.2;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.3;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.4;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.5;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.6;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.7;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.8;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
17.9;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
18;0;0;0;0;0;0;0;0;0;0;1;1;1;1;1;2;2;2;0;0;0;3000;0;0;0;0;1;1;0;1;0;0;1;0;1;1;1;0;1;0;0;0;0;0;1;1;2;2;0;0;0;1;1;1;0;0;1;1;0;1;1
|
||||
|
17
tools/vehicle_graph/examples/303e-logic.script
Normal file
17
tools/vehicle_graph/examples/303e-logic.script
Normal file
@@ -0,0 +1,17 @@
|
||||
# events for: -graph-run 303e-logic.vhg -script 303e-logic.script
|
||||
# <time, s> <operation> <target> [value]
|
||||
# operations on cab switches: press, release, toggle, inc, dec, set <position>, value <value>
|
||||
# operations on signals: force <node.port> <value>, unforce <node.port>
|
||||
0.0 toggle bat
|
||||
0.5 toggle pant_f
|
||||
6.0 press ws_on
|
||||
6.3 release ws_on
|
||||
7.0 toggle conv_sw
|
||||
8.0 toggle comp_sw
|
||||
11.0 inc reverser
|
||||
11.5 inc mainctrl
|
||||
11.6 inc mainctrl
|
||||
14.0 press ws_off
|
||||
14.2 release ws_off
|
||||
15.0 toggle pant_f
|
||||
17.0 toggle bat
|
||||
95
tools/vehicle_graph/examples/303e-logic.vhg
Normal file
95
tools/vehicle_graph/examples/303e-logic.vhg
Normal file
@@ -0,0 +1,95 @@
|
||||
// cab logic of an electric locomotive with resistor starting, modelled after the 303E (ET22) .fiz.
|
||||
// stage 1 of the vehicle graph: only logic and cab blocks are available, so the pantographs and the high voltage
|
||||
// are represented by timers and constants. the physical blocks replace them in the next stages
|
||||
{
|
||||
"format": "maszyna-vehicle-graph",
|
||||
"version": 2,
|
||||
"vehicle": { "name": "303E (logic test)" },
|
||||
"settings": { "low_voltage_simulation": false },
|
||||
"nodes": [
|
||||
{ "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw:", "command": "batterytoggle" } },
|
||||
{ "id": "pant_f", "type": "cab.switch", "title": "Front pantograph", "params": { "behaviour": "bistable", "submodel": "pantfront_sw:", "command": "pantographtogglefront" } },
|
||||
{ "id": "pant_r", "type": "cab.switch", "title": "Rear pantograph", "params": { "behaviour": "bistable", "submodel": "pantrear_sw:", "command": "pantographtogglerear" } },
|
||||
{ "id": "ws_on", "type": "cab.switch", "title": "Line breaker on", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "main_on_bt:", "command": "linebreakerclose" } },
|
||||
{ "id": "ws_off", "type": "cab.switch", "title": "Line breaker off", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "main_off_bt:", "command": "linebreakeropen" } },
|
||||
{ "id": "conv_sw", "type": "cab.switch", "title": "Converter", "params": { "behaviour": "bistable", "submodel": "converter_sw:", "command": "convertertoggle" } },
|
||||
{ "id": "comp_sw", "type": "cab.switch", "title": "Compressor", "params": { "behaviour": "bistable", "submodel": "compressor_sw:", "command": "compressortoggle" } },
|
||||
{ "id": "reverser", "type": "cab.switch", "title": "Reverser", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "labels": "back;0;forward", "step": 45, "offset": -45, "submodel": "dirkey:" } },
|
||||
{ "id": "mainctrl", "type": "cab.switch", "title": "Master controller", "params": { "behaviour": "multi", "positions": 44, "min": 0, "max": 43, "default": 0, "step": 6, "submodel": "mainctrl:" } },
|
||||
|
||||
{ "id": "pant_any", "type": "logic.or", "title": "Any pantograph selected" },
|
||||
{ "id": "pant_air", "type": "logic.and", "title": "Pantograph valve powered" },
|
||||
{ "id": "pant_up", "type": "logic.ton", "title": "Pantograph raising", "params": { "time": 4.0 } },
|
||||
{ "id": "hv_nominal", "type": "logic.const", "title": "Catenary voltage", "params": { "value": 3000 } },
|
||||
{ "id": "hv", "type": "logic.math", "title": "Voltage behind the pantographs", "params": { "op": "mul" } },
|
||||
{ "id": "hv_ok", "type": "logic.hyst", "title": "Voltage above minimum", "params": { "lo": 1800, "hi": 1900 } },
|
||||
|
||||
{ "id": "ws_close", "type": "logic.and", "title": "Closing allowed" },
|
||||
{ "id": "ws_close2", "type": "logic.and", "title": "Closing with voltage" },
|
||||
{ "id": "ws_lost", "type": "logic.nand", "title": "Battery or voltage missing" },
|
||||
{ "id": "ws_open", "type": "logic.or", "title": "Opening" },
|
||||
{ "id": "ws", "type": "logic.sr", "title": "Line breaker", "params": { "reset_priority": true } },
|
||||
{ "id": "ws_lamp_on", "type": "logic.not", "title": "Line breaker open" },
|
||||
|
||||
{ "id": "conv_run", "type": "logic.and", "title": "Converter powered" },
|
||||
{ "id": "conv", "type": "logic.ton", "title": "Converter starting", "params": { "time": 3.0 } },
|
||||
{ "id": "conv_lamp_on", "type": "logic.not", "title": "Converter off" },
|
||||
{ "id": "comp", "type": "logic.and", "title": "Compressor running" },
|
||||
|
||||
{ "id": "dir_set", "type": "logic.compare", "title": "Direction selected", "params": { "op": "ne", "value": 0 } },
|
||||
{ "id": "power_req", "type": "logic.compare", "title": "Controller above zero", "params": { "op": "gt", "value": 0 } },
|
||||
{ "id": "line_req", "type": "logic.and", "title": "Line contactors requested" },
|
||||
{ "id": "line", "type": "logic.and", "title": "Line contactors closed" },
|
||||
{ "id": "line_lamp_on", "type": "logic.not", "title": "Line contactors open" },
|
||||
|
||||
{ "id": "lamp_ws", "type": "fb.lamp", "title": "Line breaker lamp", "params": { "color": "red", "submodel": "i-mainbreaker:" } },
|
||||
{ "id": "lamp_conv", "type": "fb.lamp", "title": "Converter lamp", "params": { "color": "yellow", "submodel": "i-converteroverload:" } },
|
||||
{ "id": "lamp_line", "type": "fb.lamp", "title": "Line contactors lamp", "params": { "color": "green", "submodel": "i-stlinoff:" } },
|
||||
{ "id": "v_hv", "type": "fb.gauge", "title": "High voltage", "params": { "min": 0, "max": 4000, "unit": "V", "angle_min": 0, "angle_max": 270, "submodel": "hvoltage:" } },
|
||||
{ "id": "snd_conv", "type": "fb.sound", "title": "Converter", "params": { "file": "przetwornica.wav", "placement": "engine", "loop": true } },
|
||||
{ "id": "snd_comp", "type": "fb.sound", "title": "Compressor", "params": { "file": "sprezarka.wav", "placement": "engine", "loop": true } }
|
||||
],
|
||||
"connections": [
|
||||
{ "from": "pant_f.pos", "to": "pant_any.a" },
|
||||
{ "from": "pant_r.pos", "to": "pant_any.b" },
|
||||
{ "from": "pant_any.q", "to": "pant_air.a" },
|
||||
{ "from": "bat.pos", "to": "pant_air.b" },
|
||||
{ "from": "pant_air.q", "to": "pant_up.a" },
|
||||
{ "from": "hv_nominal.q", "to": "hv.a" },
|
||||
{ "from": "pant_up.q", "to": "hv.b" },
|
||||
{ "from": "hv.q", "to": "hv_ok.a" },
|
||||
{ "from": "hv.q", "to": "v_hv.value" },
|
||||
|
||||
{ "from": "ws_on.pos", "to": "ws_close.a" },
|
||||
{ "from": "bat.pos", "to": "ws_close.b" },
|
||||
{ "from": "ws_close.q", "to": "ws_close2.a" },
|
||||
{ "from": "hv_ok.q", "to": "ws_close2.b" },
|
||||
{ "from": "bat.pos", "to": "ws_lost.a" },
|
||||
{ "from": "hv_ok.q", "to": "ws_lost.b" },
|
||||
{ "from": "ws_off.pos", "to": "ws_open.a" },
|
||||
{ "from": "ws_lost.q", "to": "ws_open.b" },
|
||||
{ "from": "ws_close2.q", "to": "ws.s" },
|
||||
{ "from": "ws_open.q", "to": "ws.r" },
|
||||
{ "from": "ws.q", "to": "ws_lamp_on.a" },
|
||||
{ "from": "ws_lamp_on.q", "to": "lamp_ws.on" },
|
||||
|
||||
{ "from": "ws.q", "to": "conv_run.a" },
|
||||
{ "from": "conv_sw.pos", "to": "conv_run.b" },
|
||||
{ "from": "conv_run.q", "to": "conv.a" },
|
||||
{ "from": "conv.q", "to": "conv_lamp_on.a" },
|
||||
{ "from": "conv_lamp_on.q", "to": "lamp_conv.on" },
|
||||
{ "from": "conv.q", "to": "comp.a" },
|
||||
{ "from": "comp_sw.pos", "to": "comp.b" },
|
||||
{ "from": "conv.q", "to": "snd_conv.play" },
|
||||
{ "from": "comp.q", "to": "snd_comp.play" },
|
||||
|
||||
{ "from": "reverser.pos", "to": "dir_set.a" },
|
||||
{ "from": "mainctrl.pos", "to": "power_req.a" },
|
||||
{ "from": "dir_set.q", "to": "line_req.a" },
|
||||
{ "from": "power_req.q", "to": "line_req.b" },
|
||||
{ "from": "line_req.q", "to": "line.a" },
|
||||
{ "from": "ws.q", "to": "line.b" },
|
||||
{ "from": "line.q", "to": "line_lamp_on.a" },
|
||||
{ "from": "line_lamp_on.q", "to": "lamp_line.on" }
|
||||
]
|
||||
}
|
||||
253
tools/vehicle_graph/examples/vectron-logic.expected.csv
Normal file
253
tools/vehicle_graph/examples/vectron-logic.expected.csv
Normal file
@@ -0,0 +1,253 @@
|
||||
time;bat.pos;bat.index;pant_sel.pos;pant_sel.index;pant_raise.pos;pant_raise.index;pant_lower.pos;pant_lower.index;main_sw.pos;main_sw.index;reverser.pos;reverser.index;lever.pos;lever.index;pant_cmd.q;pant_cmd_on.q;pant_cmd_off.q;no_lower.q;pant_up.q;hv_nominal.q;hv.q;hv_ok.q;ms_on_req.q;ms_off_req.q;ms_init.q;ms_close.q;ms_lost.q;ms_open.q;ms.q;ms_lamp_on.q;zero.q;minus_one.q;lever_pos.q;lever_neg.q;lever_brake.q;dir_set.q;drive_ok.q;traction.q;ed_brake.q;pant_moving.q;pant_not_up.q;snd_base.q;snd_gain.q;bat:position;pant_sel:position;pant_raise:position;pant_lower:position;main_sw:position;reverser:position;lever:position;pant_cmd:state;pant_up:elapsed;ms_init:elapsed;ms:state;lamp_ms:lit;lamp_pant:lit;v_hv:value;v_demand:value;snd_inverter:playing;snd_inverter:volume;snd_inverter:pitch
|
||||
0.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
0.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;0;1;0.3;0.3;1;1;0;0;1;1;15;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
1.1;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.09;0;0;1;1;0;0;0;0.300000012;1
|
||||
1.2;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.19;0;0;1;1;0;0;0;0.300000012;1
|
||||
1.3;1;1;1;1;1;1;0;0;0;1;0;1;0;15;1;1;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;1;0;1;1;15;1;0.29;0;0;1;1;0;0;0;0.300000012;1
|
||||
1.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.39;0;0;1;1;0;0;0;0.300000012;1
|
||||
1.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.49;0;0;1;0;0;0;0;0.300000012;1
|
||||
1.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.59;0;0;1;0;0;0;0;0.300000012;1
|
||||
1.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.69;0;0;1;0;0;0;0;0.300000012;1
|
||||
1.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.79;0;0;1;0;0;0;0;0.300000012;1
|
||||
1.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.89;0;0;1;0;0;0;0;0.300000012;1
|
||||
2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;0.99;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.09;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.19;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.29;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.39;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.49;0;0;1;1;0;0;0;0.300000012;1
|
||||
2.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.59;0;0;1;0;0;0;0;0.300000012;1
|
||||
2.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.69;0;0;1;0;0;0;0;0.300000012;1
|
||||
2.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.79;0;0;1;0;0;0;0;0.300000012;1
|
||||
2.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.89;0;0;1;0;0;0;0;0.300000012;1
|
||||
3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;1.99;0;0;1;0;0;0;0;0.300000012;1
|
||||
3.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.09;0;0;1;1;0;0;0;0.300000012;1
|
||||
3.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.19;0;0;1;1;0;0;0;0.300000012;1
|
||||
3.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.29;0;0;1;1;0;0;0;0.300000012;1
|
||||
3.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.39;0;0;1;1;0;0;0;0.300000012;1
|
||||
3.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.49;0;0;1;1;0;0;0;0.300000012;1
|
||||
3.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.59;0;0;1;0;0;0;0;0.300000012;1
|
||||
3.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.69;0;0;1;0;0;0;0;0.300000012;1
|
||||
3.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.79;0;0;1;0;0;0;0;0.300000012;1
|
||||
3.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.89;0;0;1;0;0;0;0;0.300000012;1
|
||||
4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;2.99;0;0;1;0;0;0;0;0.300000012;1
|
||||
4.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.09;0;0;1;1;0;0;0;0.300000012;1
|
||||
4.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.19;0;0;1;1;0;0;0;0.300000012;1
|
||||
4.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.29;0;0;1;1;0;0;0;0.300000012;1
|
||||
4.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.39;0;0;1;1;0;0;0;0.300000012;1
|
||||
4.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.49;0;0;1;1;0;0;0;0.300000012;1
|
||||
4.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.59;0;0;1;0;0;0;0;0.300000012;1
|
||||
4.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.69;0;0;1;0;0;0;0;0.300000012;1
|
||||
4.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.79;0;0;1;0;0;0;0;0.300000012;1
|
||||
4.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.89;0;0;1;0;0;0;0;0.300000012;1
|
||||
5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;3.99;0;0;1;0;0;0;0;0.300000012;1
|
||||
5.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.09;0;0;1;1;0;0;0;0.300000012;1
|
||||
5.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.19;0;0;1;1;0;0;0;0.300000012;1
|
||||
5.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.29;0;0;1;1;0;0;0;0.300000012;1
|
||||
5.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.39;0;0;1;1;0;0;0;0.300000012;1
|
||||
5.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.49;0;0;1;1;0;0;0;0.300000012;1
|
||||
5.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.59;0;0;1;0;0;0;0;0.300000012;1
|
||||
5.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.69;0;0;1;0;0;0;0;0.300000012;1
|
||||
5.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.79;0;0;1;0;0;0;0;0.300000012;1
|
||||
5.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.89;0;0;1;0;0;0;0;0.300000012;1
|
||||
6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0;0;0;0;0;0;1;1;0.3;0.3;1;1;0;0;1;1;15;1;4.99;0;0;1;0;0;0;0;0.300000012;1
|
||||
6.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.08;0;1;0;3000;0;0;0.300000012;1
|
||||
6.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.18;0;1;0;3000;0;0;0.300000012;1
|
||||
6.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.28;0;1;0;3000;0;0;0.300000012;1
|
||||
6.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.38;0;1;0;3000;0;0;0.300000012;1
|
||||
6.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.48;0;1;0;3000;0;0;0.300000012;1
|
||||
6.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.58;0;1;0;3000;0;0;0.300000012;1
|
||||
6.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.68;0;1;0;3000;0;0;0.300000012;1
|
||||
6.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.78;0;1;0;3000;0;0;0.300000012;1
|
||||
6.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.88;0;1;0;3000;0;0;0.300000012;1
|
||||
7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;0.98;0;1;0;3000;0;0;0.300000012;1
|
||||
7.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.08;0;1;0;3000;0;0;0.300000012;1
|
||||
7.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.18;0;1;0;3000;0;0;0.300000012;1
|
||||
7.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.28;0;1;0;3000;0;0;0.300000012;1
|
||||
7.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.38;0;1;0;3000;0;0;0.300000012;1
|
||||
7.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.48;0;1;0;3000;0;0;0.300000012;1
|
||||
7.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.58;0;1;0;3000;0;0;0.300000012;1
|
||||
7.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.68;0;1;0;3000;0;0;0.300000012;1
|
||||
7.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.78;0;1;0;3000;0;0;0.300000012;1
|
||||
7.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.88;0;1;0;3000;0;0;0.300000012;1
|
||||
8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;1.98;0;1;0;3000;0;0;0.300000012;1
|
||||
8.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.08;0;1;0;3000;0;0;0.300000012;1
|
||||
8.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.18;0;1;0;3000;0;0;0.300000012;1
|
||||
8.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.28;0;1;0;3000;0;0;0.300000012;1
|
||||
8.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.38;0;1;0;3000;0;0;0.300000012;1
|
||||
8.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.48;0;1;0;3000;0;0;0.300000012;1
|
||||
8.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.58;0;1;0;3000;0;0;0.300000012;1
|
||||
8.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.68;0;1;0;3000;0;0;0.300000012;1
|
||||
8.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.78;0;1;0;3000;0;0;0.300000012;1
|
||||
8.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.88;0;1;0;3000;0;0;0.300000012;1
|
||||
9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;2.98;0;1;0;3000;0;0;0.300000012;1
|
||||
9.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.08;0;1;0;3000;0;0;0.300000012;1
|
||||
9.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.18;0;1;0;3000;0;0;0.300000012;1
|
||||
9.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.28;0;1;0;3000;0;0;0.300000012;1
|
||||
9.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.38;0;1;0;3000;0;0;0.300000012;1
|
||||
9.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.48;0;1;0;3000;0;0;0.300000012;1
|
||||
9.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.58;0;1;0;3000;0;0;0.300000012;1
|
||||
9.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.68;0;1;0;3000;0;0;0.300000012;1
|
||||
9.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.78;0;1;0;3000;0;0;0.300000012;1
|
||||
9.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.88;0;1;0;3000;0;0;0.300000012;1
|
||||
10;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;3.98;0;1;0;3000;0;0;0.300000012;1
|
||||
10.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.08;0;1;0;3000;0;0;0.300000012;1
|
||||
10.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.18;0;1;0;3000;0;0;0.300000012;1
|
||||
10.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.28;0;1;0;3000;0;0;0.300000012;1
|
||||
10.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.38;0;1;0;3000;0;0;0.300000012;1
|
||||
10.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.48;0;1;0;3000;0;0;0.300000012;1
|
||||
10.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.58;0;1;0;3000;0;0;0.300000012;1
|
||||
10.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.68;0;1;0;3000;0;0;0.300000012;1
|
||||
10.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.78;0;1;0;3000;0;0;0.300000012;1
|
||||
10.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.88;0;1;0;3000;0;0;0.300000012;1
|
||||
11;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;4.98;0;1;0;3000;0;0;0.300000012;1
|
||||
11.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.08;0;1;0;3000;0;0;0.300000012;1
|
||||
11.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.18;0;1;0;3000;0;0;0.300000012;1
|
||||
11.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.28;0;1;0;3000;0;0;0.300000012;1
|
||||
11.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.38;0;1;0;3000;0;0;0.300000012;1
|
||||
11.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.48;0;1;0;3000;0;0;0.300000012;1
|
||||
11.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.58;0;1;0;3000;0;0;0.300000012;1
|
||||
11.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.68;0;1;0;3000;0;0;0.300000012;1
|
||||
11.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.78;0;1;0;3000;0;0;0.300000012;1
|
||||
11.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.88;0;1;0;3000;0;0;0.300000012;1
|
||||
12;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;5.98;0;1;0;3000;0;0;0.300000012;1
|
||||
12.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.08;0;1;0;3000;0;0;0.300000012;1
|
||||
12.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.18;0;1;0;3000;0;0;0.300000012;1
|
||||
12.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.28;0;1;0;3000;0;0;0.300000012;1
|
||||
12.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.38;0;1;0;3000;0;0;0.300000012;1
|
||||
12.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.48;0;1;0;3000;0;0;0.300000012;1
|
||||
12.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.58;0;1;0;3000;0;0;0.300000012;1
|
||||
12.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.68;0;1;0;3000;0;0;0.300000012;1
|
||||
12.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.78;0;1;0;3000;0;0;0.300000012;1
|
||||
12.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.88;0;1;0;3000;0;0;0.300000012;1
|
||||
13;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;6.98;0;1;0;3000;0;0;0.300000012;1
|
||||
13.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.08;0;1;0;3000;0;0;0.300000012;1
|
||||
13.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.18;0;1;0;3000;0;0;0.300000012;1
|
||||
13.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.28;0;1;0;3000;0;0;0.300000012;1
|
||||
13.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.38;0;1;0;3000;0;0;0.300000012;1
|
||||
13.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.48;0;1;0;3000;0;0;0.300000012;1
|
||||
13.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.58;0;1;0;3000;0;0;0.300000012;1
|
||||
13.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.68;0;1;0;3000;0;0;0.300000012;1
|
||||
13.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.78;0;1;0;3000;0;0;0.300000012;1
|
||||
13.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.88;0;1;0;3000;0;0;0.300000012;1
|
||||
14;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;7.98;0;1;0;3000;0;0;0.300000012;1
|
||||
14.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.08;0;1;0;3000;0;0;0.300000012;1
|
||||
14.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.18;0;1;0;3000;0;0;0.300000012;1
|
||||
14.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.28;0;1;0;3000;0;0;0.300000012;1
|
||||
14.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.38;0;1;0;3000;0;0;0.300000012;1
|
||||
14.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.48;0;1;0;3000;0;0;0.300000012;1
|
||||
14.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.58;0;1;0;3000;0;0;0.300000012;1
|
||||
14.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.68;0;1;0;3000;0;0;0.300000012;1
|
||||
14.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.78;0;1;0;3000;0;0;0.300000012;1
|
||||
14.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.88;0;1;0;3000;0;0;0.300000012;1
|
||||
15;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;8.98;0;1;0;3000;0;0;0.300000012;1
|
||||
15.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.08;0;1;0;3000;0;0;0.300000012;1
|
||||
15.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.18;0;1;0;3000;0;0;0.300000012;1
|
||||
15.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.28;0;1;0;3000;0;0;0.300000012;1
|
||||
15.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.38;0;1;0;3000;0;0;0.300000012;1
|
||||
15.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.48;0;1;0;3000;0;0;0.300000012;1
|
||||
15.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.58;0;1;0;3000;0;0;0.300000012;1
|
||||
15.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.68;0;1;0;3000;0;0;0.300000012;1
|
||||
15.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.78;0;1;0;3000;0;0;0.300000012;1
|
||||
15.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.88;0;1;0;3000;0;0;0.300000012;1
|
||||
16;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;0;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;9.98;0;1;0;3000;0;0;0.300000012;1
|
||||
16.1;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.2;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.3;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.4;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.5;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
16.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
17;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
17.1;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.2;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.3;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.4;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.5;1;1;1;1;0;0;0;0;1;2;0;1;0;15;1;0;0;1;1;3000;3000;1;1;0;1;1;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;2;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.6;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.7;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.8;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
17.9;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18;1;1;1;1;0;0;0;0;0;1;0;1;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;0;0;0;0;0;0;0.3;0.3;1;1;0;0;1;1;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.1;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.2;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.3;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.4;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.5;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.6;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.7;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.8;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
18.9;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
19;1;1;1;1;0;0;0;0;0;1;1;2;0;15;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0;0;1;1;0;0;0;0;0.3;0.3;1;1;0;0;1;2;15;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
19.1;1;1;1;1;0;0;0;0;0;1;1;2;0.0666666667;16;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.0666666667;-0.0666666667;0;1;1;0.0666666667;0;0;0;0.3;0.366666667;1;1;0;0;1;2;16;1;5;10;1;0;0;3000;0.0666666701;1;0.366666675;1
|
||||
19.2;1;1;1;1;0;0;0;0;0;1;1;2;0.133333333;17;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.133333333;-0.133333333;0;1;1;0.133333333;0;0;0;0.3;0.433333333;1;1;0;0;1;2;17;1;5;10;1;0;0;3000;0.13333334;1;0.433333337;1
|
||||
19.3;1;1;1;1;0;0;0;0;0;1;1;2;0.2;18;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.2;-0.2;0;1;1;0.2;0;0;0;0.3;0.5;1;1;0;0;1;2;18;1;5;10;1;0;0;3000;0.200000003;1;0.5;1
|
||||
19.4;1;1;1;1;0;0;0;0;0;1;1;2;0.266666667;19;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.266666667;-0.266666667;0;1;1;0.266666667;0;0;0;0.3;0.566666667;1;1;0;0;1;2;19;1;5;10;1;0;0;3000;0.266666681;1;0.566666663;1
|
||||
19.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
19.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
19.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
19.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
19.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.1;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.2;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.3;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.4;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.5;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.6;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.7;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.8;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
20.9;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
21;1;1;1;1;0;0;0;0;0;1;1;2;0.333333333;20;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0.333333333;-0.333333333;0;1;1;0.333333333;0;0;0;0.3;0.633333333;1;1;0;0;1;2;20;1;5;10;1;0;0;3000;0.333333343;1;0.633333325;1
|
||||
21.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
21.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
22.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
23;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;1;0;0;-1;0;0.333333333;0.333333333;1;1;0;0.333333333;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;1;0;0;3000;0;1;0.300000012;1
|
||||
23.1;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.2;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.3;1;1;1;1;0;0;0;0;-1;0;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;1;1;0;0;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;0;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
23.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
24;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;1;0;0;1;1;3000;3000;1;0;0;1;0;0;0;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;0;0.3;0.3;1;1;0;0;1;2;10;1;5;10;0;1;0;3000;0;0;0.300000012;1
|
||||
24.1;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.2;1;1;1;1;0;0;1;1;0;1;1;2;-0.333333333;10;0;0;1;0;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;1;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.3;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.4;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.5;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.6;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.7;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.8;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
24.9;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
25;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
25.1;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
25.2;1;1;1;1;0;0;0;0;0;1;1;2;-0.333333333;10;0;0;0;1;0;3000;0;0;0;0;0;0;1;1;0;1;0;-1;0;0.333333333;0.333333333;1;0;0;0;0;1;0.3;0.3;1;1;0;0;1;2;10;0;0;0;0;1;0;0;0;0;0.300000012;1
|
||||
|
17
tools/vehicle_graph/examples/vectron-logic.script
Normal file
17
tools/vehicle_graph/examples/vectron-logic.script
Normal file
@@ -0,0 +1,17 @@
|
||||
# events for: -graph-run vectron-logic.vhg -script vectron-logic.script
|
||||
0.0 toggle bat
|
||||
1.0 press pant_raise
|
||||
1.3 release pant_raise
|
||||
17.0 inc main_sw
|
||||
17.5 release main_sw
|
||||
18.0 inc reverser
|
||||
19.0 inc lever
|
||||
19.1 inc lever
|
||||
19.2 inc lever
|
||||
19.3 inc lever
|
||||
19.4 inc lever
|
||||
21.0 set lever 10
|
||||
23.0 dec main_sw
|
||||
23.3 release main_sw
|
||||
24.0 press pant_lower
|
||||
24.2 release pant_lower
|
||||
112
tools/vehicle_graph/examples/vectron-logic.vhg
Normal file
112
tools/vehicle_graph/examples/vectron-logic.vhg
Normal file
@@ -0,0 +1,112 @@
|
||||
// cab logic of an electric locomotive with induction motors, modelled after the Vectron .fiz.
|
||||
// stage 1 of the vehicle graph: only logic and cab blocks are available, so the pantograph, the main circuit
|
||||
// and the inverters are represented by timers and constants. the combined power/brake lever is split into
|
||||
// a traction demand and an electrodynamic brake demand, which will drive the inverter blocks in the next stages
|
||||
{
|
||||
"format": "maszyna-vehicle-graph",
|
||||
"version": 2,
|
||||
"vehicle": { "name": "Vectron (logic test)" },
|
||||
"settings": { "low_voltage_simulation": false },
|
||||
"nodes": [
|
||||
{ "id": "bat", "type": "cab.switch", "title": "Battery", "params": { "behaviour": "bistable", "submodel": "battery_sw:", "command": "batterytoggle" } },
|
||||
{ "id": "pant_sel", "type": "cab.switch", "title": "Pantograph selector", "params": { "behaviour": "multi", "positions": 4, "min": 0, "max": 3, "default": 1, "labels": "rear;front;both;rear", "step": 30, "submodel": "pantselect_sw:" } },
|
||||
{ "id": "pant_raise", "type": "cab.switch", "title": "Raise pantographs", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "pantselectedraise_bt:", "command": "pantographraiseselected" } },
|
||||
{ "id": "pant_lower", "type": "cab.switch", "title": "Lower pantographs", "params": { "behaviour": "monostable", "animation": "move", "step": 4, "submodel": "pantselectedlower_bt:", "command": "pantographlowerselected" } },
|
||||
{ "id": "main_sw", "type": "cab.switch", "title": "Main switch", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "spring": true, "labels": "off;0;on", "step": 40, "offset": -40, "submodel": "main_sw:" } },
|
||||
{ "id": "reverser", "type": "cab.switch", "title": "Direction", "params": { "behaviour": "multi", "positions": 3, "min": -1, "max": 1, "default": 0, "labels": "back;0;forward", "step": 45, "offset": -45, "submodel": "dirkey:" } },
|
||||
{ "id": "lever", "type": "cab.switch", "title": "Power/brake lever", "params": { "behaviour": "multi", "positions": 31, "min": -1, "max": 1, "default": 0, "step": 2, "offset": -30, "submodel": "jointctrl:" } },
|
||||
|
||||
{ "id": "pant_cmd", "type": "logic.sr", "title": "Pantographs commanded up" },
|
||||
{ "id": "pant_cmd_on", "type": "logic.and", "title": "Raising with battery" },
|
||||
{ "id": "pant_cmd_off", "type": "logic.nand", "title": "Lowering or battery off" },
|
||||
{ "id": "no_lower", "type": "logic.not", "title": "Lower button released" },
|
||||
{ "id": "pant_up", "type": "logic.ton", "title": "Pantograph raising", "params": { "time": 5.0 } },
|
||||
{ "id": "hv_nominal", "type": "logic.const", "title": "Catenary voltage", "params": { "value": 3000 } },
|
||||
{ "id": "hv", "type": "logic.math", "title": "Voltage behind the pantograph", "params": { "op": "mul" } },
|
||||
{ "id": "hv_ok", "type": "logic.hyst", "title": "Voltage above minimum", "params": { "lo": 1800, "hi": 1900 } },
|
||||
|
||||
{ "id": "ms_on_req", "type": "logic.compare", "title": "Main switch to on", "params": { "op": "gt", "value": 0 } },
|
||||
{ "id": "ms_off_req", "type": "logic.compare", "title": "Main switch to off", "params": { "op": "lt", "value": 0 } },
|
||||
{ "id": "ms_init", "type": "logic.ton", "title": "Main circuit initialisation", "params": { "time": 10.0 } },
|
||||
{ "id": "ms_close", "type": "logic.and", "title": "Closing allowed" },
|
||||
{ "id": "ms_lost", "type": "logic.nand", "title": "Battery or voltage missing" },
|
||||
{ "id": "ms_open", "type": "logic.or", "title": "Opening" },
|
||||
{ "id": "ms", "type": "logic.sr", "title": "Main circuit breaker" },
|
||||
{ "id": "ms_lamp_on", "type": "logic.not", "title": "Main circuit open" },
|
||||
|
||||
{ "id": "zero", "type": "logic.const", "title": "Zero", "params": { "value": 0 } },
|
||||
{ "id": "minus_one", "type": "logic.const", "title": "Minus one", "params": { "value": -1 } },
|
||||
{ "id": "lever_pos", "type": "logic.math", "title": "Traction part", "params": { "op": "max" } },
|
||||
{ "id": "lever_neg", "type": "logic.math", "title": "Lever inverted", "params": { "op": "mul" } },
|
||||
{ "id": "lever_brake", "type": "logic.math", "title": "Brake part", "params": { "op": "max" } },
|
||||
{ "id": "dir_set", "type": "logic.compare", "title": "Direction selected", "params": { "op": "ne", "value": 0 } },
|
||||
{ "id": "drive_ok", "type": "logic.and", "title": "Ready to drive" },
|
||||
{ "id": "traction", "type": "logic.select", "title": "Traction demand" },
|
||||
{ "id": "ed_brake", "type": "logic.select", "title": "ED brake demand" },
|
||||
|
||||
{ "id": "lamp_ms", "type": "fb.lamp", "title": "Main circuit lamp", "params": { "color": "red", "submodel": "i-mainbreaker:" } },
|
||||
{ "id": "lamp_pant", "type": "fb.lamp", "title": "Pantograph lamp", "params": { "color": "yellow", "blink": true, "blink_period": 1.0, "submodel": "i-pantraising:" } },
|
||||
{ "id": "pant_moving", "type": "logic.and", "title": "Pantograph moving" },
|
||||
{ "id": "pant_not_up", "type": "logic.not", "title": "Pantograph not up yet" },
|
||||
{ "id": "v_hv", "type": "fb.gauge", "title": "Catenary voltage", "params": { "min": 0, "max": 4000, "unit": "V", "angle_min": 0, "angle_max": 270, "submodel": "hvoltage:" } },
|
||||
{ "id": "v_demand", "type": "fb.gauge", "title": "Traction demand", "params": { "min": 0, "max": 1, "angle_min": 0, "angle_max": 270, "submodel": "tractionforce:" } },
|
||||
|
||||
{ "id": "snd_base", "type": "logic.const", "title": "Idle volume", "params": { "value": 0.3 } },
|
||||
{ "id": "snd_gain", "type": "logic.math", "title": "Volume rising with demand", "params": { "op": "add" } },
|
||||
{ "id": "snd_inverter", "type": "fb.sound", "title": "Inverters", "params": { "file": "vectron-inverter.wav", "placement": "engine", "loop": true } }
|
||||
],
|
||||
"connections": [
|
||||
{ "from": "pant_raise.pos", "to": "pant_cmd_on.a" },
|
||||
{ "from": "bat.pos", "to": "pant_cmd_on.b" },
|
||||
{ "from": "pant_lower.pos", "to": "no_lower.a" },
|
||||
{ "from": "no_lower.q", "to": "pant_cmd_off.a" },
|
||||
{ "from": "bat.pos", "to": "pant_cmd_off.b" },
|
||||
{ "from": "pant_cmd_on.q", "to": "pant_cmd.s" },
|
||||
{ "from": "pant_cmd_off.q", "to": "pant_cmd.r" },
|
||||
{ "from": "pant_cmd.q", "to": "pant_up.a" },
|
||||
{ "from": "hv_nominal.q", "to": "hv.a" },
|
||||
{ "from": "pant_up.q", "to": "hv.b" },
|
||||
{ "from": "hv.q", "to": "hv_ok.a" },
|
||||
{ "from": "hv.q", "to": "v_hv.value" },
|
||||
{ "from": "pant_up.q", "to": "pant_not_up.a" },
|
||||
{ "from": "pant_cmd.q", "to": "pant_moving.a" },
|
||||
{ "from": "pant_not_up.q", "to": "pant_moving.b" },
|
||||
{ "from": "pant_moving.q", "to": "lamp_pant.on" },
|
||||
|
||||
{ "from": "main_sw.pos", "to": "ms_on_req.a" },
|
||||
{ "from": "main_sw.pos", "to": "ms_off_req.a" },
|
||||
{ "from": "hv_ok.q", "to": "ms_init.a" },
|
||||
{ "from": "ms_on_req.q", "to": "ms_close.a" },
|
||||
{ "from": "ms_init.q", "to": "ms_close.b" },
|
||||
{ "from": "bat.pos", "to": "ms_lost.a" },
|
||||
{ "from": "hv_ok.q", "to": "ms_lost.b" },
|
||||
{ "from": "ms_off_req.q", "to": "ms_open.a" },
|
||||
{ "from": "ms_lost.q", "to": "ms_open.b" },
|
||||
{ "from": "ms_close.q", "to": "ms.s" },
|
||||
{ "from": "ms_open.q", "to": "ms.r" },
|
||||
{ "from": "ms.q", "to": "ms_lamp_on.a" },
|
||||
{ "from": "ms_lamp_on.q", "to": "lamp_ms.on" },
|
||||
|
||||
{ "from": "lever.pos", "to": "lever_pos.a" },
|
||||
{ "from": "zero.q", "to": "lever_pos.b" },
|
||||
{ "from": "lever.pos", "to": "lever_neg.a" },
|
||||
{ "from": "minus_one.q", "to": "lever_neg.b" },
|
||||
{ "from": "lever_neg.q", "to": "lever_brake.a" },
|
||||
{ "from": "zero.q", "to": "lever_brake.b" },
|
||||
{ "from": "reverser.pos", "to": "dir_set.a" },
|
||||
{ "from": "dir_set.q", "to": "drive_ok.a" },
|
||||
{ "from": "ms.q", "to": "drive_ok.b" },
|
||||
{ "from": "drive_ok.q", "to": "traction.sel" },
|
||||
{ "from": "zero.q", "to": "traction.a" },
|
||||
{ "from": "lever_pos.q", "to": "traction.b" },
|
||||
{ "from": "drive_ok.q", "to": "ed_brake.sel" },
|
||||
{ "from": "zero.q", "to": "ed_brake.a" },
|
||||
{ "from": "lever_brake.q", "to": "ed_brake.b" },
|
||||
{ "from": "traction.q", "to": "v_demand.value" },
|
||||
|
||||
{ "from": "ms.q", "to": "snd_inverter.play" },
|
||||
{ "from": "snd_base.q", "to": "snd_gain.a" },
|
||||
{ "from": "traction.q", "to": "snd_gain.b" },
|
||||
{ "from": "snd_gain.q", "to": "snd_inverter.volume" }
|
||||
]
|
||||
}
|
||||
96
tools/vehicle_graph/run_tests.py
Normal file
96
tools/vehicle_graph/run_tests.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Tests of the vehicle graph core, run through the simulator's -graph-run command.
|
||||
|
||||
usage: run_tests.py <simulator executable> [--update]
|
||||
|
||||
- every tests/*.vhg file starts with a line "// expect: ok", "// expect: error <text>" or "// expect: warning <text>",
|
||||
and is checked against the output of -graph-run;
|
||||
- every <name>.vhg with a <name>.script, in both directories, is run twice, and both results have to be identical (determinism)
|
||||
and equal to <name>.expected.csv. --update writes the current results as the expected ones.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def run(executable, *arguments):
|
||||
result = subprocess.run([str(executable), "-graph-run", *map(str, arguments)], capture_output=True, text=True)
|
||||
return result.returncode, result.stdout + result.stderr
|
||||
|
||||
|
||||
def check_file(executable, path):
|
||||
first = path.read_text(encoding="utf-8").splitlines()[0]
|
||||
if not first.startswith("// expect:"):
|
||||
return f"{path.name}: missing '// expect:' line"
|
||||
expectation = first[len("// expect:"):].strip()
|
||||
kind, _, text = expectation.partition(" ")
|
||||
code, output = run(executable, path)
|
||||
if kind == "ok":
|
||||
if code != 0 or "error" in output:
|
||||
return f"{path.name}: expected no errors, got:\n{output}"
|
||||
elif kind == "error":
|
||||
if code == 0 or text not in output:
|
||||
return f"{path.name}: expected error containing '{text}', got (exit {code}):\n{output}"
|
||||
elif kind == "warning":
|
||||
if code != 0 or "warning" not in output or text not in output:
|
||||
return f"{path.name}: expected warning containing '{text}', got (exit {code}):\n{output}"
|
||||
else:
|
||||
return f"{path.name}: unknown expectation '{expectation}'"
|
||||
return None
|
||||
|
||||
|
||||
def check_example(executable, path, update):
|
||||
script = path.with_suffix(".script")
|
||||
expected = path.with_suffix(".expected.csv")
|
||||
results = []
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
for attempt in range(2):
|
||||
output = pathlib.Path(directory) / f"run{attempt}.csv"
|
||||
code, log = run(executable, path, "-script", script, "-every", 10, "-o", output)
|
||||
if code != 0:
|
||||
return f"{path.name}: run failed:\n{log}"
|
||||
# git may check the expected files out with windows line endings
|
||||
results.append(output.read_bytes().replace(b"\r\n", b"\n"))
|
||||
if results[0] != results[1]:
|
||||
return f"{path.name}: two runs gave different results"
|
||||
if update:
|
||||
expected.write_bytes(results[0])
|
||||
return None
|
||||
if not expected.exists():
|
||||
return f"{path.name}: missing {expected.name}, run with --update to create it"
|
||||
if expected.read_bytes().replace(b"\r\n", b"\n") != results[0]:
|
||||
return f"{path.name}: results differ from {expected.name}"
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return 2
|
||||
executable = pathlib.Path(sys.argv[1]).resolve()
|
||||
update = "--update" in sys.argv[2:]
|
||||
failures = []
|
||||
count = 0
|
||||
for path in sorted((ROOT / "tests").glob("*.vhg")):
|
||||
count += 1
|
||||
problem = check_file(executable, path)
|
||||
if problem:
|
||||
failures.append(problem)
|
||||
for path in sorted((ROOT / "tests").glob("*.vhg")) + sorted((ROOT / "examples").glob("*.vhg")):
|
||||
if path.with_suffix(".script").exists():
|
||||
count += 1
|
||||
problem = check_example(executable, path, update)
|
||||
if problem:
|
||||
failures.append(problem)
|
||||
for problem in failures:
|
||||
print("FAIL", problem)
|
||||
print(f"{count - len(failures)} of {count} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
2
tools/vehicle_graph/tests/bad-choice.vhg
Normal file
2
tools/vehicle_graph/tests/bad-choice.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error has to be one of
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "sw", "type": "cab.switch", "params": { "behaviour": "bistabilny" } } ] }
|
||||
2
tools/vehicle_graph/tests/bad-param-type.vhg
Normal file
2
tools/vehicle_graph/tests/bad-param-type.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error has to be a number
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "sw", "type": "cab.switch", "params": { "positions": "3" } } ] }
|
||||
36
tools/vehicle_graph/tests/blocks.expected.csv
Normal file
36
tools/vehicle_graph/tests/blocks.expected.csv
Normal file
@@ -0,0 +1,36 @@
|
||||
time;x.q;y.q;and.q;or.q;xor.q;nand.q;nor.q;not.q;sr.q;sr_set.q;ton.q;tof.q;delay.q;hyst.q;le.q;div.q;min.q;sel.q;button.pos;button.index;lever.pos;lever.index;sr:state;sr_set:state;ton:elapsed;tof:elapsed;button:position;lever:position
|
||||
0.1;1;0;0;1;1;1;0;0;1;1;0;1;1;0;1;0;0;1;0;0;0;2;1;1;0.1;0;0;2
|
||||
0.2;1;0;0;1;1;1;0;0;1;1;0;1;1;0;1;0;0;1;0;0;0;2;1;1;0.2;0;0;2
|
||||
0.3;1;0;0;1;1;1;0;0;1;1;0;1;1;0;1;0;0;1;0;0;0;2;1;1;0.3;0;0;2
|
||||
0.4;1;1;1;1;0;0;0;0;0;1;0;1;1;0;1;1;1;1;0;0;0;2;0;1;0.4;0;0;2
|
||||
0.5;1;1;1;1;0;0;0;0;0;1;1;1;1;0;1;1;1;1;0;0;0;2;0;1;0.5;0;0;2
|
||||
0.6;1;1;1;1;0;0;0;0;0;1;1;1;1;0;1;1;1;1;0;0;0;2;0;1;0.5;0;0;2
|
||||
0.7;0;1;0;1;1;1;0;1;0;0;0;1;0;0;1;0;0;0;0;0;0;2;0;0;0;0.1;0;2
|
||||
0.8;0;1;0;1;1;1;0;1;0;0;0;1;0;0;1;0;0;2;1;1;2;4;0;0;0;0.2;1;4
|
||||
0.9;0;1;0;1;1;1;0;1;0;0;0;1;0;0;1;0;0;2;1;1;2;4;0;0;0;0.3;1;4
|
||||
1;0;1;0;1;1;1;0;1;0;0;0;1;0;0;1;0;0;0;0;0;2;4;0;0;0;0.4;0;4
|
||||
1.1;0;1;0;1;1;1;0;1;0;0;0;0;0;0;1;0;0;0;0;0;0;2;0;0;0;0.5;0;2
|
||||
1.2;3;1;1;1;0;0;0;0;0;1;0;1;3;0;1;3;1;3;0;0;0;2;0;1;0.1;0;0;2
|
||||
1.3;5;1;1;1;0;0;0;0;0;1;0;1;5;1;0;5;1;5;0;0;0;2;0;1;0.2;0;0;2
|
||||
1.4;2.5;1;1;1;0;0;0;0;0;1;0;1;2.5;1;1;2.5;1;2.5;0;0;0;2;0;1;0.3;0;0;2
|
||||
1.5;1;1;1;1;0;0;0;0;0;1;0;1;1;0;1;1;1;1;0;0;0;2;0;1;0.4;0;0;2
|
||||
1.6;1;0;0;1;1;1;0;0;1;1;1;1;1;0;1;0;0;1;0;0;0;2;1;1;0.5;0;0;2
|
||||
1.7;1;0;0;1;1;1;0;0;1;1;1;1;1;0;1;0;0;1;0;0;-2;0;1;1;0.5;0;0;0
|
||||
1.8;0;0;0;0;0;1;1;1;1;1;0;1;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.1;0;0
|
||||
1.9;0;0;0;0;0;1;1;1;1;1;0;1;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.2;0;0
|
||||
2;0;0;0;0;0;1;1;1;1;1;0;1;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.3;0;0
|
||||
2.1;0;0;0;0;0;1;1;1;1;1;0;1;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.4;0;0
|
||||
2.2;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.5;0;0
|
||||
2.3;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.5;0;0
|
||||
2.4;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.5;0;0
|
||||
2.5;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;0;0;0;-2;0;1;1;0;0.5;0;0
|
||||
2.6;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
2.7;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
2.8;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
2.9;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3.1;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3.2;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3.3;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3.4;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
3.5;0;0;0;0;0;1;1;1;1;1;0;0;0;0;1;0;0;-2;1;1;-2;0;1;1;0;0.5;1;0
|
||||
|
16
tools/vehicle_graph/tests/blocks.script
Normal file
16
tools/vehicle_graph/tests/blocks.script
Normal file
@@ -0,0 +1,16 @@
|
||||
0.0 force x.q 1
|
||||
0.3 force y.q 1
|
||||
0.6 force x.q 0
|
||||
0.7 press button
|
||||
0.71 inc lever
|
||||
0.72 inc lever
|
||||
0.9 release button
|
||||
1.0 release lever
|
||||
1.1 force x.q 3
|
||||
1.2 force x.q 5
|
||||
1.3 force x.q 2.5
|
||||
1.4 force x.q 1
|
||||
1.5 force y.q 0
|
||||
1.6 value lever -1.9
|
||||
1.7 unforce x.q
|
||||
2.5 toggle button
|
||||
39
tools/vehicle_graph/tests/blocks.vhg
Normal file
39
tools/vehicle_graph/tests/blocks.vhg
Normal file
@@ -0,0 +1,39 @@
|
||||
// expect: ok
|
||||
// every logic block driven by forced inputs; see blocks.script
|
||||
{
|
||||
"format": "maszyna-vehicle-graph", "version": 2,
|
||||
"nodes": [
|
||||
{ "id": "x", "type": "logic.const", "params": { "value": 0 } },
|
||||
{ "id": "y", "type": "logic.const", "params": { "value": 0 } },
|
||||
{ "id": "and", "type": "logic.and" }, { "id": "or", "type": "logic.or" }, { "id": "xor", "type": "logic.xor" },
|
||||
{ "id": "nand", "type": "logic.nand" }, { "id": "nor", "type": "logic.nor" }, { "id": "not", "type": "logic.not" },
|
||||
{ "id": "sr", "type": "logic.sr" },
|
||||
{ "id": "sr_set", "type": "logic.sr", "params": { "reset_priority": false } },
|
||||
{ "id": "ton", "type": "logic.ton", "params": { "time": 0.5 } },
|
||||
{ "id": "tof", "type": "logic.tof", "params": { "time": 0.5 } },
|
||||
{ "id": "delay", "type": "logic.delay", "params": { "initial": 7 } },
|
||||
{ "id": "hyst", "type": "logic.hyst", "params": { "lo": 2, "hi": 4 } },
|
||||
{ "id": "le", "type": "logic.compare", "params": { "op": "le", "value": 3 } },
|
||||
{ "id": "div", "type": "logic.math", "params": { "op": "div" } },
|
||||
{ "id": "min", "type": "logic.math", "params": { "op": "min" } },
|
||||
{ "id": "sel", "type": "logic.select" },
|
||||
{ "id": "button", "type": "cab.switch", "params": { "behaviour": "monostable" } },
|
||||
{ "id": "lever", "type": "cab.switch", "params": { "behaviour": "multi", "positions": 5, "min": -2, "max": 2, "default": 0, "spring": true } }
|
||||
],
|
||||
"connections": [
|
||||
{ "from": "x.q", "to": "and.a" }, { "from": "y.q", "to": "and.b" },
|
||||
{ "from": "x.q", "to": "or.a" }, { "from": "y.q", "to": "or.b" },
|
||||
{ "from": "x.q", "to": "xor.a" }, { "from": "y.q", "to": "xor.b" },
|
||||
{ "from": "x.q", "to": "nand.a" }, { "from": "y.q", "to": "nand.b" },
|
||||
{ "from": "x.q", "to": "nor.a" }, { "from": "y.q", "to": "nor.b" },
|
||||
{ "from": "x.q", "to": "not.a" },
|
||||
{ "from": "x.q", "to": "sr.s" }, { "from": "y.q", "to": "sr.r" },
|
||||
{ "from": "x.q", "to": "sr_set.s" }, { "from": "y.q", "to": "sr_set.r" },
|
||||
{ "from": "x.q", "to": "ton.a" }, { "from": "x.q", "to": "tof.a" },
|
||||
{ "from": "x.q", "to": "delay.a" },
|
||||
{ "from": "x.q", "to": "hyst.a" }, { "from": "x.q", "to": "le.a" },
|
||||
{ "from": "x.q", "to": "div.a" }, { "from": "y.q", "to": "div.b" },
|
||||
{ "from": "x.q", "to": "min.a" }, { "from": "y.q", "to": "min.b" },
|
||||
{ "from": "button.pos", "to": "sel.sel" }, { "from": "x.q", "to": "sel.a" }, { "from": "lever.pos", "to": "sel.b" }
|
||||
]
|
||||
}
|
||||
3
tools/vehicle_graph/tests/domain-mismatch.vhg
Normal file
3
tools/vehicle_graph/tests/domain-mismatch.vhg
Normal file
@@ -0,0 +1,3 @@
|
||||
// expect: error joins different domains
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "a", "type": "cab.switch" }, { "id": "lamp", "type": "fb.lamp" } ],
|
||||
"connections": [ { "from": "a.pos", "to": "lamp.supply" } ] }
|
||||
2
tools/vehicle_graph/tests/duplicate-id.vhg
Normal file
2
tools/vehicle_graph/tests/duplicate-id.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error duplicate node id
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "a", "type": "logic.not" }, { "id": "a", "type": "logic.not" } ] }
|
||||
15
tools/vehicle_graph/tests/loop-deferred.vhg
Normal file
15
tools/vehicle_graph/tests/loop-deferred.vhg
Normal file
@@ -0,0 +1,15 @@
|
||||
// expect: ok
|
||||
// the same self-holding circuit as loop.vhg, closed through a unit delay
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [
|
||||
{ "id": "start", "type": "cab.switch" },
|
||||
{ "id": "hold", "type": "logic.or" },
|
||||
{ "id": "coil", "type": "logic.and" },
|
||||
{ "id": "stop", "type": "logic.const", "params": { "value": 1 } },
|
||||
{ "id": "contact", "type": "logic.delay" }
|
||||
], "connections": [
|
||||
{ "from": "start.pos", "to": "hold.a" },
|
||||
{ "from": "contact.q", "to": "hold.b" },
|
||||
{ "from": "hold.q", "to": "coil.a" },
|
||||
{ "from": "stop.q", "to": "coil.b" },
|
||||
{ "from": "coil.q", "to": "contact.a" }
|
||||
] }
|
||||
12
tools/vehicle_graph/tests/loop.vhg
Normal file
12
tools/vehicle_graph/tests/loop.vhg
Normal file
@@ -0,0 +1,12 @@
|
||||
// expect: error loop without a deferred block
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [
|
||||
{ "id": "start", "type": "cab.switch" },
|
||||
{ "id": "hold", "type": "logic.or" },
|
||||
{ "id": "coil", "type": "logic.and" },
|
||||
{ "id": "stop", "type": "logic.const", "params": { "value": 1 } }
|
||||
], "connections": [
|
||||
{ "from": "start.pos", "to": "hold.a" },
|
||||
{ "from": "coil.q", "to": "hold.b" },
|
||||
{ "from": "hold.q", "to": "coil.a" },
|
||||
{ "from": "stop.q", "to": "coil.b" }
|
||||
] }
|
||||
2
tools/vehicle_graph/tests/malformed.vhg
Normal file
2
tools/vehicle_graph/tests/malformed.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error malformed file
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": 5, "type": "logic.not" } ] }
|
||||
4
tools/vehicle_graph/tests/two-sources.vhg
Normal file
4
tools/vehicle_graph/tests/two-sources.vhg
Normal file
@@ -0,0 +1,4 @@
|
||||
// expect: error has more than one source
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [
|
||||
{ "id": "a", "type": "cab.switch" }, { "id": "b", "type": "cab.switch" }, { "id": "n", "type": "logic.not" }
|
||||
], "connections": [ { "from": "a.pos", "to": "n.a" }, { "from": "b.pos", "to": "n.a" } ] }
|
||||
2
tools/vehicle_graph/tests/unconnected.vhg
Normal file
2
tools/vehicle_graph/tests/unconnected.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: warning required input "a" isn't connected
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "n", "type": "logic.not" } ] }
|
||||
2
tools/vehicle_graph/tests/unknown-type.vhg
Normal file
2
tools/vehicle_graph/tests/unknown-type.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error unknown block type
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "x", "type": "el.flux_capacitor" } ] }
|
||||
2
tools/vehicle_graph/tests/version-1.vhg
Normal file
2
tools/vehicle_graph/tests/version-1.vhg
Normal file
@@ -0,0 +1,2 @@
|
||||
// expect: error unsupported file version 1
|
||||
{ "format": "maszyna-vehicle-graph", "version": 1, "nodes": [] }
|
||||
3
tools/vehicle_graph/tests/wrong-direction.vhg
Normal file
3
tools/vehicle_graph/tests/wrong-direction.vhg
Normal file
@@ -0,0 +1,3 @@
|
||||
// expect: error isn't an output
|
||||
{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "a", "type": "logic.not" }, { "id": "b", "type": "logic.not" } ],
|
||||
"connections": [ { "from": "a.a", "to": "b.a" } ] }
|
||||
381
vehicle/graph/blocks/cab.cpp
Normal file
381
vehicle/graph/blocks/cab.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/blocks/cab.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::vector<std::string> split_labels(std::string const &Text)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
if (Text.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
std::string::size_type start{0};
|
||||
while (true)
|
||||
{
|
||||
auto const separator{Text.find(';', start)};
|
||||
result.emplace_back(Text.substr(start, separator == std::string::npos ? std::string::npos : separator - start));
|
||||
if (separator == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
start = separator + 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// cab_switch
|
||||
|
||||
cab_switch::cab_switch(block_setup const &Setup)
|
||||
: block(Setup), m_behaviour(static_cast<behaviour_type>(choice(Setup, param::behaviour))),
|
||||
m_positioncount(std::max(1, static_cast<int>(std::lround(number(Setup, param::positions))))), m_min(number(Setup, param::min)), m_max(number(Setup, param::max)),
|
||||
m_spring(boolean(Setup, param::spring)), m_labels(split_labels(text(Setup, param::labels))), m_animation(static_cast<animation_type>(choice(Setup, param::animation))),
|
||||
m_step(number(Setup, param::step)), m_offset(number(Setup, param::offset)), m_smoothing(number(Setup, param::smoothing)), m_submodel(text(Setup, param::submodel)),
|
||||
m_keyincrease(text(Setup, param::key_inc)), m_keydecrease(text(Setup, param::key_dec)), m_command(text(Setup, param::command)), m_hardwarename(text(Setup, param::hw_name))
|
||||
{
|
||||
// the default is given as a value, as the author sees it; the switch starts at the position closest to it
|
||||
auto const defaultvalue{number(Setup, param::default_value)};
|
||||
if (m_positioncount > 1 && m_max != m_min)
|
||||
{
|
||||
m_defaultposition = clamp_position(static_cast<int>(std::lround((defaultvalue - m_min) / (m_max - m_min) * (m_positioncount - 1))));
|
||||
}
|
||||
m_position = m_defaultposition;
|
||||
}
|
||||
|
||||
int cab_switch::clamp_position(int const Position) const
|
||||
{
|
||||
return std::clamp(Position, 0, m_positioncount - 1);
|
||||
}
|
||||
|
||||
double cab_switch::value_at(int const Position) const
|
||||
{
|
||||
if (m_positioncount < 2)
|
||||
{
|
||||
return m_min;
|
||||
}
|
||||
return m_min + (m_max - m_min) * clamp_position(Position) / (m_positioncount - 1);
|
||||
}
|
||||
|
||||
std::string cab_switch::label(int const Position) const
|
||||
{
|
||||
return (Position >= 0 && Position < static_cast<int>(m_labels.size()) ? m_labels[Position] : std::string{});
|
||||
}
|
||||
|
||||
void cab_switch::update(frame &Frame)
|
||||
{
|
||||
set(Frame, port::value, value_at(m_position));
|
||||
set(Frame, port::index, m_position);
|
||||
}
|
||||
|
||||
void cab_switch::increase()
|
||||
{
|
||||
m_position = clamp_position(m_position + 1);
|
||||
}
|
||||
|
||||
void cab_switch::decrease()
|
||||
{
|
||||
m_position = clamp_position(m_position - 1);
|
||||
}
|
||||
|
||||
void cab_switch::toggle()
|
||||
{
|
||||
switch (m_behaviour)
|
||||
{
|
||||
case behaviour_type::monostable:
|
||||
{
|
||||
// a momentary switch can't stay toggled; a toggle is treated as pressing it, the release brings it back
|
||||
press();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
auto const other{m_defaultposition == m_positioncount - 1 ? 0 : m_positioncount - 1};
|
||||
m_position = (m_position == m_defaultposition ? other : m_defaultposition);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cab_switch::press()
|
||||
{
|
||||
switch (m_behaviour)
|
||||
{
|
||||
case behaviour_type::multi:
|
||||
{
|
||||
// levers are moved with increase and decrease; pressing them has no meaning
|
||||
break;
|
||||
}
|
||||
case behaviour_type::monostable:
|
||||
{
|
||||
m_position = (m_defaultposition == m_positioncount - 1 ? 0 : m_positioncount - 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
toggle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cab_switch::release()
|
||||
{
|
||||
if (m_behaviour == behaviour_type::monostable || (m_behaviour == behaviour_type::multi && m_spring))
|
||||
{
|
||||
m_position = m_defaultposition;
|
||||
}
|
||||
}
|
||||
|
||||
void cab_switch::set_position(int const Position)
|
||||
{
|
||||
m_position = clamp_position(Position);
|
||||
}
|
||||
|
||||
void cab_switch::set_value(double const Value)
|
||||
{
|
||||
if (m_positioncount < 2 || m_max == m_min)
|
||||
{
|
||||
return;
|
||||
}
|
||||
set_position(static_cast<int>(std::lround((Value - m_min) / (m_max - m_min) * (m_positioncount - 1))));
|
||||
}
|
||||
|
||||
block_type cab_switch::describe()
|
||||
{
|
||||
block_type type;
|
||||
type.id = "cab.switch";
|
||||
type.category = "cab";
|
||||
type.name = "Switch";
|
||||
type.description = "Switch, button or lever of the cab. Its behaviour and animation are defined here, and its value can be connected to any block.";
|
||||
type.origin = block_origin::cab;
|
||||
type.ports = {signal_out("pos", value_type::real), signal_out("index", value_type::integer)};
|
||||
type.params = {
|
||||
choice_param("behaviour", {"bistable", "monostable", "multi"}, "bistable"),
|
||||
number_param("positions", 2.0),
|
||||
number_param("min", 0.0),
|
||||
number_param("max", 1.0),
|
||||
number_param("default", 0.0),
|
||||
boolean_param("spring", false),
|
||||
text_param("labels"),
|
||||
choice_param("animation", {"rotate", "move"}, "rotate"),
|
||||
number_param("step", 45.0, "deg/mm"),
|
||||
number_param("offset", 0.0, "deg/mm"),
|
||||
number_param("smoothing", 0.2, "s"),
|
||||
text_param("submodel"),
|
||||
text_param("key_inc"),
|
||||
text_param("key_dec"),
|
||||
text_param("command"),
|
||||
text_param("hw_name"),
|
||||
};
|
||||
type.create = make_block<cab_switch>;
|
||||
return type;
|
||||
}
|
||||
|
||||
// cab_lamp
|
||||
|
||||
cab_lamp::cab_lamp(block_setup const &Setup)
|
||||
: block(Setup), m_supplied(is_connected(Setup, port::supply)), m_blink(boolean(Setup, param::blink)), m_blinkperiod(std::max(0.01, number(Setup, param::blink_period))),
|
||||
m_minvoltage(number(Setup, param::min_voltage)), m_power(std::max(0.0, number(Setup, param::power))), m_submodel(text(Setup, param::submodel))
|
||||
{
|
||||
}
|
||||
|
||||
void cab_lamp::update(frame &Frame)
|
||||
{
|
||||
// without low voltage simulation, or without the supply connected, the lamp is assumed to be powered
|
||||
auto const lowvoltage{Frame.low_voltage && m_supplied};
|
||||
auto const voltage{lowvoltage ? forward_in(Frame, port::supply) : 0.0};
|
||||
auto const powered{false == lowvoltage || voltage >= m_minvoltage};
|
||||
auto lit{powered && get_bool(Frame, port::on)};
|
||||
if (lit && m_blink)
|
||||
{
|
||||
// blinking follows the time of the graph, so all lamps of the vehicle blink in step
|
||||
lit = std::fmod(Frame.time, m_blinkperiod) < 0.5 * m_blinkperiod;
|
||||
}
|
||||
m_lit = lit;
|
||||
if (lowvoltage && m_lit && voltage > 1.0)
|
||||
{
|
||||
add_back(Frame, port::supply, m_power / voltage);
|
||||
}
|
||||
}
|
||||
|
||||
block_type cab_lamp::describe()
|
||||
{
|
||||
block_type type;
|
||||
type.id = "fb.lamp";
|
||||
type.category = "indicators";
|
||||
type.name = "Lamp";
|
||||
type.description = "Indicator lamp. With low voltage simulation enabled and the supply connected, it needs enough voltage to light, and draws current.";
|
||||
type.origin = block_origin::cab;
|
||||
type.ports = {signal_in("on", value_type::boolean), physical_in("supply", domain::low_voltage, true)};
|
||||
type.params = {
|
||||
choice_param("color", {"green", "yellow", "red", "white", "blue"}, "green"),
|
||||
boolean_param("blink", false),
|
||||
number_param("blink_period", 1.0, "s"),
|
||||
number_param("min_voltage", 70.0, "V"),
|
||||
number_param("power", 2.0, "W"),
|
||||
text_param("submodel"),
|
||||
};
|
||||
type.create = make_block<cab_lamp>;
|
||||
return type;
|
||||
}
|
||||
|
||||
// cab_gauge
|
||||
|
||||
cab_gauge::cab_gauge(block_setup const &Setup, bool const Twoneedles)
|
||||
: block(Setup), m_twoneedles(Twoneedles), m_min(number(Setup, param::min)), m_max(number(Setup, param::max)), m_anglemin(number(Setup, param::angle_min)),
|
||||
m_anglemax(number(Setup, param::angle_max)), m_smoothing(number(Setup, param::smoothing)), m_submodel(text(Setup, param::submodel)),
|
||||
m_submodel2(Twoneedles ? text(Setup, param::submodel2) : std::string{})
|
||||
{
|
||||
m_values[0] = m_values[1] = static_cast<float>(std::min(m_min, m_max));
|
||||
}
|
||||
|
||||
void cab_gauge::update(frame &Frame)
|
||||
{
|
||||
// the needle stops at the ends of the scale
|
||||
auto const low{std::min(m_min, m_max)};
|
||||
auto const high{std::max(m_min, m_max)};
|
||||
m_values[0] = static_cast<float>(std::clamp(get(Frame, port::value), low, high));
|
||||
if (m_twoneedles)
|
||||
{
|
||||
m_values[1] = static_cast<float>(std::clamp(get(Frame, port::value2), low, high));
|
||||
}
|
||||
}
|
||||
|
||||
block_type cab_gauge::describe(bool const Twoneedles)
|
||||
{
|
||||
block_type type;
|
||||
type.category = "indicators";
|
||||
type.origin = block_origin::cab;
|
||||
if (Twoneedles)
|
||||
{
|
||||
type.id = "fb.manometer";
|
||||
type.name = "Two needle gauge";
|
||||
type.description = "Gauge with two needles, e.g. pressure gauge of the brake pipe (black) and the main reservoir (red).";
|
||||
type.ports = {signal_in("black", value_type::real, 0.0, "bar"), signal_in("red", value_type::real, 0.0, "bar", true)};
|
||||
}
|
||||
else
|
||||
{
|
||||
type.id = "fb.gauge";
|
||||
type.name = "Gauge";
|
||||
type.description = "Needle gauge: voltmeter, ammeter, speedometer.";
|
||||
type.ports = {signal_in("value", value_type::real)};
|
||||
}
|
||||
type.params = {
|
||||
number_param("min", 0.0),
|
||||
number_param("max", Twoneedles ? 10.0 : 100.0),
|
||||
text_param("unit", Twoneedles ? "bar" : ""),
|
||||
number_param("angle_min", -135.0, "deg"),
|
||||
number_param("angle_max", 135.0, "deg"),
|
||||
number_param("smoothing", 0.3, "s"),
|
||||
text_param("submodel"),
|
||||
};
|
||||
if (Twoneedles)
|
||||
{
|
||||
type.params.push_back(text_param("submodel2"));
|
||||
}
|
||||
if (Twoneedles)
|
||||
{
|
||||
type.create = [](block_setup const &Setup) -> std::unique_ptr<block> { return std::make_unique<cab_gauge>(Setup, true); };
|
||||
}
|
||||
else
|
||||
{
|
||||
type.create = [](block_setup const &Setup) -> std::unique_ptr<block> { return std::make_unique<cab_gauge>(Setup, false); };
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
// vehicle_sound
|
||||
|
||||
vehicle_sound::vehicle_sound(block_setup const &Setup)
|
||||
: block(Setup), m_file(text(Setup, param::file)), m_basevolume(number(Setup, param::volume)), m_basepitch(number(Setup, param::pitch)), m_loop(boolean(Setup, param::loop)),
|
||||
m_placement(static_cast<placement_type>(choice(Setup, param::placement))), m_range(number(Setup, param::range)),
|
||||
m_offset{number(Setup, param::offset_x), number(Setup, param::offset_y), number(Setup, param::offset_z)}
|
||||
{
|
||||
}
|
||||
|
||||
void vehicle_sound::update(frame &Frame)
|
||||
{
|
||||
m_playing = get_bool(Frame, port::play);
|
||||
m_volume = static_cast<float>(std::max(0.0, m_basevolume * get(Frame, port::volume)));
|
||||
m_pitch = static_cast<float>(std::max(0.0, m_basepitch * get(Frame, port::pitch)));
|
||||
}
|
||||
|
||||
char const *vehicle_sound::state_name(std::size_t const Index) const
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
return "playing";
|
||||
case 1:
|
||||
return "volume";
|
||||
default:
|
||||
return "pitch";
|
||||
}
|
||||
}
|
||||
|
||||
double vehicle_sound::state_value(std::size_t const Index) const
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0:
|
||||
return m_playing ? 1.0 : 0.0;
|
||||
case 1:
|
||||
return m_volume;
|
||||
default:
|
||||
return m_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
block_type vehicle_sound::describe()
|
||||
{
|
||||
block_type type;
|
||||
type.id = "fb.sound";
|
||||
type.category = "indicators";
|
||||
type.name = "Sound";
|
||||
type.description = "Sound of the vehicle or of its cab. Volume and pitch are the parameters multiplied by the inputs; looped sounds play while the play input is on.";
|
||||
type.origin = block_origin::cab;
|
||||
type.ports = {signal_in("play", value_type::boolean), signal_in("volume", value_type::real, 1.0, "", true), signal_in("pitch", value_type::real, 1.0, "", true)};
|
||||
type.params = {
|
||||
text_param("file"),
|
||||
number_param("volume", 1.0),
|
||||
number_param("pitch", 1.0),
|
||||
boolean_param("loop", true),
|
||||
choice_param("placement", {"general", "internal", "engine", "external", "external_ambient"}, "internal"),
|
||||
number_param("range", -1.0, "m"),
|
||||
number_param("offset_x", 0.0, "m"),
|
||||
number_param("offset_y", 0.0, "m"),
|
||||
number_param("offset_z", 0.0, "m"),
|
||||
};
|
||||
type.create = make_block<vehicle_sound>;
|
||||
return type;
|
||||
}
|
||||
|
||||
void register_cab_blocks(catalog &Catalog)
|
||||
{
|
||||
Catalog.add(cab_switch::describe());
|
||||
Catalog.add(cab_lamp::describe());
|
||||
Catalog.add(cab_gauge::describe(false));
|
||||
Catalog.add(cab_gauge::describe(true));
|
||||
Catalog.add(vehicle_sound::describe());
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
441
vehicle/graph/blocks/cab.h
Normal file
441
vehicle/graph/blocks/cab.h
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_block.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// cab controls and indicators. the blocks hold the logic and the values; the cab (TTrain) creates the animated
|
||||
// submodels from their parameters and binds them to the values these blocks expose
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
// switch, button or lever of the cab. its behaviour and animation are defined in the graph, and its value
|
||||
// is available to the other blocks
|
||||
class cab_switch final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
value, // "pos": value of the current position, between min and max
|
||||
index // number of the current position, from 0
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
behaviour,
|
||||
positions,
|
||||
min,
|
||||
max,
|
||||
default_value,
|
||||
spring,
|
||||
labels,
|
||||
animation,
|
||||
step,
|
||||
offset,
|
||||
smoothing,
|
||||
submodel,
|
||||
key_inc,
|
||||
key_dec,
|
||||
command,
|
||||
hw_name
|
||||
};
|
||||
};
|
||||
enum class behaviour_type
|
||||
{
|
||||
bistable, // stays where it was put
|
||||
monostable, // returns to the default position when released, e.g. a push button
|
||||
multi // several positions; with the spring enabled it returns to the default position when released
|
||||
};
|
||||
enum class animation_type
|
||||
{
|
||||
rotate,
|
||||
move
|
||||
};
|
||||
|
||||
explicit cab_switch(block_setup const &Setup);
|
||||
|
||||
void update(frame &Frame) override;
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
char const *state_name(std::size_t) const override
|
||||
{
|
||||
return "position";
|
||||
}
|
||||
double state_value(std::size_t) const override
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
// operations of the player, AI or hardware. they take effect in the next update
|
||||
void increase();
|
||||
void decrease();
|
||||
void toggle();
|
||||
void press();
|
||||
void release();
|
||||
void set_position(int Position);
|
||||
// moves to the position with the value closest to the specified one, e.g. for hardware levers
|
||||
void set_value(double Value);
|
||||
|
||||
int position() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
int position_count() const
|
||||
{
|
||||
return m_positioncount;
|
||||
}
|
||||
int default_position() const
|
||||
{
|
||||
return m_defaultposition;
|
||||
}
|
||||
double value_at(int Position) const;
|
||||
behaviour_type behaviour() const
|
||||
{
|
||||
return m_behaviour;
|
||||
}
|
||||
bool spring() const
|
||||
{
|
||||
return m_spring;
|
||||
}
|
||||
// description of the specified position, from the labels parameter; empty if not specified
|
||||
std::string label(int Position) const;
|
||||
// animation of the submodel: offset + step * position, in degrees or millimetres
|
||||
animation_type animation() const
|
||||
{
|
||||
return m_animation;
|
||||
}
|
||||
double step() const
|
||||
{
|
||||
return m_step;
|
||||
}
|
||||
double offset() const
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
double smoothing() const
|
||||
{
|
||||
return m_smoothing;
|
||||
}
|
||||
std::string const &submodel() const
|
||||
{
|
||||
return m_submodel;
|
||||
}
|
||||
// bindings: keys, existing simulator command, name for the hardware desk
|
||||
std::string const &key_increase() const
|
||||
{
|
||||
return m_keyincrease;
|
||||
}
|
||||
std::string const &key_decrease() const
|
||||
{
|
||||
return m_keydecrease;
|
||||
}
|
||||
std::string const &command() const
|
||||
{
|
||||
return m_command;
|
||||
}
|
||||
std::string const &hardware_name() const
|
||||
{
|
||||
return m_hardwarename;
|
||||
}
|
||||
|
||||
static block_type describe();
|
||||
|
||||
private:
|
||||
int clamp_position(int Position) const;
|
||||
|
||||
behaviour_type const m_behaviour;
|
||||
int const m_positioncount;
|
||||
double const m_min;
|
||||
double const m_max;
|
||||
int m_defaultposition{0};
|
||||
bool const m_spring;
|
||||
std::vector<std::string> m_labels;
|
||||
animation_type const m_animation;
|
||||
double const m_step;
|
||||
double const m_offset;
|
||||
double const m_smoothing;
|
||||
std::string const m_submodel;
|
||||
std::string const m_keyincrease;
|
||||
std::string const m_keydecrease;
|
||||
std::string const m_command;
|
||||
std::string const m_hardwarename;
|
||||
int m_position{0};
|
||||
};
|
||||
|
||||
// indicator lamp. when the graph simulates low voltage circuits and the supply input is connected, the lamp lights
|
||||
// only with enough voltage, and draws current from the supply
|
||||
class cab_lamp final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
on,
|
||||
supply
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
color,
|
||||
blink,
|
||||
blink_period,
|
||||
min_voltage,
|
||||
power,
|
||||
submodel
|
||||
};
|
||||
};
|
||||
|
||||
explicit cab_lamp(block_setup const &Setup);
|
||||
|
||||
void update(frame &Frame) override;
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
char const *state_name(std::size_t) const override
|
||||
{
|
||||
return "lit";
|
||||
}
|
||||
double state_value(std::size_t) const override
|
||||
{
|
||||
return m_lit ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
// stable address of the state, for TButton::AssignBool
|
||||
bool const *lit_state() const
|
||||
{
|
||||
return &m_lit;
|
||||
}
|
||||
std::string const &submodel() const
|
||||
{
|
||||
return m_submodel;
|
||||
}
|
||||
|
||||
static block_type describe();
|
||||
|
||||
private:
|
||||
bool const m_supplied; // whether the supply input is connected
|
||||
bool const m_blink;
|
||||
double const m_blinkperiod;
|
||||
double const m_minvoltage;
|
||||
double const m_power;
|
||||
std::string const m_submodel;
|
||||
bool m_lit{false};
|
||||
};
|
||||
|
||||
// needle gauge with one or two needles: voltmeter, ammeter, speedometer, pressure gauge
|
||||
class cab_gauge final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
value,
|
||||
value2
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
min,
|
||||
max,
|
||||
unit,
|
||||
angle_min,
|
||||
angle_max,
|
||||
smoothing,
|
||||
submodel,
|
||||
submodel2
|
||||
};
|
||||
};
|
||||
|
||||
cab_gauge(block_setup const &Setup, bool Twoneedles);
|
||||
|
||||
void update(frame &Frame) override;
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return m_twoneedles ? 2 : 1;
|
||||
}
|
||||
char const *state_name(std::size_t Index) const override
|
||||
{
|
||||
return Index == 0 ? "value" : "value2";
|
||||
}
|
||||
double state_value(std::size_t Index) const override
|
||||
{
|
||||
return m_values[Index];
|
||||
}
|
||||
|
||||
// stable addresses of the values shown by the needles, clamped to the scale, for TGauge::AssignFloat
|
||||
float *value_state(std::size_t Needle)
|
||||
{
|
||||
return &m_values[Needle];
|
||||
}
|
||||
bool two_needles() const
|
||||
{
|
||||
return m_twoneedles;
|
||||
}
|
||||
double min() const
|
||||
{
|
||||
return m_min;
|
||||
}
|
||||
double max() const
|
||||
{
|
||||
return m_max;
|
||||
}
|
||||
double angle_min() const
|
||||
{
|
||||
return m_anglemin;
|
||||
}
|
||||
double angle_max() const
|
||||
{
|
||||
return m_anglemax;
|
||||
}
|
||||
double smoothing() const
|
||||
{
|
||||
return m_smoothing;
|
||||
}
|
||||
std::string const &submodel(std::size_t Needle) const
|
||||
{
|
||||
return Needle == 0 ? m_submodel : m_submodel2;
|
||||
}
|
||||
|
||||
static block_type describe(bool Twoneedles);
|
||||
|
||||
private:
|
||||
bool const m_twoneedles;
|
||||
double const m_min;
|
||||
double const m_max;
|
||||
double const m_anglemin;
|
||||
double const m_anglemax;
|
||||
double const m_smoothing;
|
||||
std::string const m_submodel;
|
||||
std::string const m_submodel2;
|
||||
float m_values[2]{0.f, 0.f};
|
||||
};
|
||||
|
||||
// sound of the vehicle or of its cab. sounds of a graph vehicle are defined in the graph instead of the .mmd file
|
||||
class vehicle_sound final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
play,
|
||||
volume,
|
||||
pitch
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
file,
|
||||
volume,
|
||||
pitch,
|
||||
loop,
|
||||
placement,
|
||||
range,
|
||||
offset_x,
|
||||
offset_y,
|
||||
offset_z
|
||||
};
|
||||
};
|
||||
// the same placements as sound_placement of the audio subsystem
|
||||
enum class placement_type
|
||||
{
|
||||
general,
|
||||
internal,
|
||||
engine,
|
||||
external,
|
||||
external_ambient
|
||||
};
|
||||
|
||||
explicit vehicle_sound(block_setup const &Setup);
|
||||
|
||||
void update(frame &Frame) override;
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
char const *state_name(std::size_t Index) const override;
|
||||
double state_value(std::size_t Index) const override;
|
||||
|
||||
bool playing() const
|
||||
{
|
||||
return m_playing;
|
||||
}
|
||||
// volume and pitch: parameter multiplied by the input
|
||||
float volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
float pitch() const
|
||||
{
|
||||
return m_pitch;
|
||||
}
|
||||
std::string const &file() const
|
||||
{
|
||||
return m_file;
|
||||
}
|
||||
bool loop() const
|
||||
{
|
||||
return m_loop;
|
||||
}
|
||||
placement_type placement() const
|
||||
{
|
||||
return m_placement;
|
||||
}
|
||||
double range() const
|
||||
{
|
||||
return m_range;
|
||||
}
|
||||
double offset_x() const
|
||||
{
|
||||
return m_offset[0];
|
||||
}
|
||||
double offset_y() const
|
||||
{
|
||||
return m_offset[1];
|
||||
}
|
||||
double offset_z() const
|
||||
{
|
||||
return m_offset[2];
|
||||
}
|
||||
|
||||
static block_type describe();
|
||||
|
||||
private:
|
||||
std::string const m_file;
|
||||
double const m_basevolume;
|
||||
double const m_basepitch;
|
||||
bool const m_loop;
|
||||
placement_type const m_placement;
|
||||
double const m_range;
|
||||
double const m_offset[3];
|
||||
bool m_playing{false};
|
||||
float m_volume{0.f};
|
||||
float m_pitch{1.f};
|
||||
};
|
||||
|
||||
} // namespace vgraph
|
||||
625
vehicle/graph/blocks/logic.cpp
Normal file
625
vehicle/graph/blocks/logic.cpp
Normal file
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
// generic logic and arithmetic blocks. boolean signals are stored as 0 and 1, and read as true above 0.5
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// tolerance of the equality comparisons; switch positions and constants are compared, not results of long computations
|
||||
double constexpr equality_tolerance{1e-6};
|
||||
|
||||
block_type make_type(std::string Id, std::string Name, std::string Description)
|
||||
{
|
||||
block_type type;
|
||||
type.id = std::move(Id);
|
||||
type.category = "logic";
|
||||
type.name = std::move(Name);
|
||||
type.description = std::move(Description);
|
||||
type.origin = block_origin::primitive;
|
||||
return type;
|
||||
}
|
||||
|
||||
class logic_compare final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
op,
|
||||
value
|
||||
};
|
||||
};
|
||||
enum class operation
|
||||
{
|
||||
ne,
|
||||
eq,
|
||||
lt,
|
||||
le,
|
||||
gt,
|
||||
ge
|
||||
};
|
||||
|
||||
explicit logic_compare(block_setup const &Setup) : block(Setup), m_operation(static_cast<operation>(choice(Setup, param::op))), m_value(number(Setup, param::value)) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const a{get(Frame, port::a)};
|
||||
auto result{false};
|
||||
switch (m_operation)
|
||||
{
|
||||
case operation::ne:
|
||||
result = std::abs(a - m_value) > equality_tolerance;
|
||||
break;
|
||||
case operation::eq:
|
||||
result = std::abs(a - m_value) <= equality_tolerance;
|
||||
break;
|
||||
case operation::lt:
|
||||
result = a < m_value;
|
||||
break;
|
||||
case operation::le:
|
||||
result = a <= m_value + equality_tolerance;
|
||||
break;
|
||||
case operation::gt:
|
||||
result = a > m_value;
|
||||
break;
|
||||
case operation::ge:
|
||||
result = a >= m_value - equality_tolerance;
|
||||
break;
|
||||
}
|
||||
set_bool(Frame, port::q, result);
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.compare", "Compare", "Compares the input with a constant value.")};
|
||||
type.ports = {signal_in("a", value_type::real), signal_out("q", value_type::boolean)};
|
||||
type.params = {choice_param("op", {"ne", "eq", "lt", "le", "gt", "ge"}, "ne"), number_param("value", 0.0)};
|
||||
type.create = make_block<logic_compare>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
operation const m_operation;
|
||||
double const m_value;
|
||||
};
|
||||
|
||||
enum class gate
|
||||
{
|
||||
and_,
|
||||
or_,
|
||||
xor_,
|
||||
nand_,
|
||||
nor_
|
||||
};
|
||||
|
||||
class logic_gate final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
b,
|
||||
q
|
||||
};
|
||||
};
|
||||
|
||||
logic_gate(block_setup const &Setup, gate const Gate) : block(Setup), m_gate(Gate) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const a{get_bool(Frame, port::a)};
|
||||
auto const b{get_bool(Frame, port::b)};
|
||||
auto result{false};
|
||||
switch (m_gate)
|
||||
{
|
||||
case gate::and_:
|
||||
result = a && b;
|
||||
break;
|
||||
case gate::or_:
|
||||
result = a || b;
|
||||
break;
|
||||
case gate::xor_:
|
||||
result = a != b;
|
||||
break;
|
||||
case gate::nand_:
|
||||
result = false == (a && b);
|
||||
break;
|
||||
case gate::nor_:
|
||||
result = false == (a || b);
|
||||
break;
|
||||
}
|
||||
set_bool(Frame, port::q, result);
|
||||
}
|
||||
|
||||
template <gate Gate_> static std::unique_ptr<block> create(block_setup const &Setup)
|
||||
{
|
||||
return std::make_unique<logic_gate>(Setup, Gate_);
|
||||
}
|
||||
|
||||
template <gate Gate_> static block_type describe(std::string Id, std::string Name, double const Default)
|
||||
{
|
||||
// unconnected inputs default to the neutral value of the gate, so a gate can be used with one input
|
||||
auto type{make_type(std::move(Id), std::move(Name), "Logic gate.")};
|
||||
type.ports = {signal_in("a", value_type::boolean, Default), signal_in("b", value_type::boolean, Default, "", true), signal_out("q", value_type::boolean)};
|
||||
type.create = create<Gate_>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
gate const m_gate;
|
||||
};
|
||||
|
||||
class logic_not final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
q
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_not(block_setup const &Setup) : block(Setup) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
set_bool(Frame, port::q, false == get_bool(Frame, port::a));
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.not", "NOT", "Negation.")};
|
||||
type.ports = {signal_in("a", value_type::boolean), signal_out("q", value_type::boolean)};
|
||||
type.create = make_block<logic_not>;
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
// set-reset latch. it's deferred, so it can close loops, e.g. self-holding contactor circuits
|
||||
class logic_sr final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
s,
|
||||
r,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
reset_priority,
|
||||
initial
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_sr(block_setup const &Setup) : block(Setup), m_resetpriority(boolean(Setup, param::reset_priority)), m_state(boolean(Setup, param::initial)) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const s{get_bool(Frame, port::s)};
|
||||
auto const r{get_bool(Frame, port::r)};
|
||||
if (s && r)
|
||||
{
|
||||
m_state = (false == m_resetpriority);
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
m_state = true;
|
||||
}
|
||||
else if (r)
|
||||
{
|
||||
m_state = false;
|
||||
}
|
||||
}
|
||||
|
||||
void commit(frame &Frame) override
|
||||
{
|
||||
set_bool(Frame, port::q, m_state);
|
||||
}
|
||||
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
char const *state_name(std::size_t) const override
|
||||
{
|
||||
return "state";
|
||||
}
|
||||
double state_value(std::size_t) const override
|
||||
{
|
||||
return m_state ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.sr", "SR latch", "Set sets the output, reset clears it. The output changes in the next step, so the block can close loops.")};
|
||||
type.deferred = true;
|
||||
type.ports = {signal_in("s", value_type::boolean), signal_in("r", value_type::boolean, 0.0, "", true), signal_out("q", value_type::boolean)};
|
||||
type.params = {boolean_param("reset_priority", true), boolean_param("initial", false)};
|
||||
type.create = make_block<logic_sr>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
bool const m_resetpriority;
|
||||
bool m_state;
|
||||
};
|
||||
|
||||
// on-delay (TON) and off-delay (TOF) timers
|
||||
class logic_timer final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
time
|
||||
};
|
||||
};
|
||||
|
||||
logic_timer(block_setup const &Setup, bool const Ondelay) : block(Setup), m_ondelay(Ondelay), m_delay(std::max(0.0, number(Setup, param::time))) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const a{get_bool(Frame, port::a)};
|
||||
if (a == m_ondelay)
|
||||
{
|
||||
m_elapsed = std::min(m_elapsed + Frame.dt, m_delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_elapsed = 0.0;
|
||||
}
|
||||
auto const expired{m_elapsed >= m_delay};
|
||||
m_output = (m_ondelay ? (a && expired) : (a || false == expired));
|
||||
}
|
||||
|
||||
void commit(frame &Frame) override
|
||||
{
|
||||
set_bool(Frame, port::q, m_output);
|
||||
}
|
||||
|
||||
std::size_t state_count() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
char const *state_name(std::size_t) const override
|
||||
{
|
||||
return "elapsed";
|
||||
}
|
||||
double state_value(std::size_t) const override
|
||||
{
|
||||
return m_elapsed;
|
||||
}
|
||||
|
||||
template <bool Ondelay_> static std::unique_ptr<block> create(block_setup const &Setup)
|
||||
{
|
||||
return std::make_unique<logic_timer>(Setup, Ondelay_);
|
||||
}
|
||||
|
||||
template <bool Ondelay_> static block_type describe()
|
||||
{
|
||||
auto type{Ondelay_ ? make_type("logic.ton", "On delay", "The output follows the input switching on after the specified time, and switching off immediately.")
|
||||
: make_type("logic.tof", "Off delay", "The output follows the input switching on immediately, and switching off after the specified time.")};
|
||||
type.deferred = true;
|
||||
type.ports = {signal_in("a", value_type::boolean), signal_out("q", value_type::boolean)};
|
||||
type.params = {number_param("time", 1.0, "s")};
|
||||
type.create = create<Ondelay_>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
bool const m_ondelay;
|
||||
double const m_delay;
|
||||
// for the on-delay: time the input is on; for the off-delay: time the input is off
|
||||
// (both start expired for the off-delay, so the output is off until the input is switched on)
|
||||
double m_elapsed{m_ondelay ? 0.0 : m_delay};
|
||||
bool m_output{false};
|
||||
};
|
||||
|
||||
// delays the signal by one step; the explicit way to close a loop
|
||||
class logic_delay final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
initial
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_delay(block_setup const &Setup) : block(Setup), m_value(number(Setup, param::initial)) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
m_value = get(Frame, port::a);
|
||||
}
|
||||
|
||||
void commit(frame &Frame) override
|
||||
{
|
||||
set(Frame, port::q, m_value);
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.delay", "Unit delay", "Passes the input to the output one step later. Use it to close loops.")};
|
||||
type.deferred = true;
|
||||
type.ports = {signal_in("a", value_type::real), signal_out("q", value_type::real)};
|
||||
type.params = {number_param("initial", 0.0)};
|
||||
type.create = make_block<logic_delay>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_value;
|
||||
};
|
||||
|
||||
class logic_hysteresis final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
lo,
|
||||
hi,
|
||||
initial
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_hysteresis(block_setup const &Setup)
|
||||
: block(Setup), m_low(number(Setup, param::lo)), m_high(number(Setup, param::hi)), m_state(boolean(Setup, param::initial))
|
||||
{
|
||||
}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const a{get(Frame, port::a)};
|
||||
if (a >= m_high)
|
||||
{
|
||||
m_state = true;
|
||||
}
|
||||
else if (a <= m_low)
|
||||
{
|
||||
m_state = false;
|
||||
}
|
||||
set_bool(Frame, port::q, m_state);
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.hyst", "Hysteresis", "Switches on when the input reaches the upper threshold, and off when it drops to the lower one.")};
|
||||
type.ports = {signal_in("a", value_type::real), signal_out("q", value_type::boolean)};
|
||||
type.params = {number_param("lo", 0.0), number_param("hi", 1.0), boolean_param("initial", false)};
|
||||
type.create = make_block<logic_hysteresis>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
double const m_low;
|
||||
double const m_high;
|
||||
bool m_state;
|
||||
};
|
||||
|
||||
class logic_const final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
value
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_const(block_setup const &Setup) : block(Setup), m_value(number(Setup, param::value)) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
set(Frame, port::q, m_value);
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.const", "Constant", "Constant value.")};
|
||||
type.ports = {signal_out("q", value_type::real)};
|
||||
type.params = {number_param("value", 1.0)};
|
||||
type.create = make_block<logic_const>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
double const m_value;
|
||||
};
|
||||
|
||||
class logic_math final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
a,
|
||||
b,
|
||||
q
|
||||
};
|
||||
};
|
||||
struct param
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
op
|
||||
};
|
||||
};
|
||||
enum class operation
|
||||
{
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
div,
|
||||
min,
|
||||
max
|
||||
};
|
||||
|
||||
explicit logic_math(block_setup const &Setup) : block(Setup), m_operation(static_cast<operation>(choice(Setup, param::op))) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
auto const a{get(Frame, port::a)};
|
||||
auto const b{get(Frame, port::b)};
|
||||
auto result{0.0};
|
||||
switch (m_operation)
|
||||
{
|
||||
case operation::add:
|
||||
result = a + b;
|
||||
break;
|
||||
case operation::sub:
|
||||
result = a - b;
|
||||
break;
|
||||
case operation::mul:
|
||||
result = a * b;
|
||||
break;
|
||||
case operation::div:
|
||||
// division by zero gives zero rather than infinity, which would spread through the whole graph
|
||||
result = (b != 0.0 ? a / b : 0.0);
|
||||
break;
|
||||
case operation::min:
|
||||
result = std::min(a, b);
|
||||
break;
|
||||
case operation::max:
|
||||
result = std::max(a, b);
|
||||
break;
|
||||
}
|
||||
set(Frame, port::q, result);
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.math", "Arithmetic", "Arithmetic operation on two inputs. Division by zero gives zero.")};
|
||||
type.ports = {signal_in("a", value_type::real), signal_in("b", value_type::real, 1.0), signal_out("q", value_type::real)};
|
||||
type.params = {choice_param("op", {"add", "sub", "mul", "div", "min", "max"}, "mul")};
|
||||
type.create = make_block<logic_math>;
|
||||
return type;
|
||||
}
|
||||
|
||||
private:
|
||||
operation const m_operation;
|
||||
};
|
||||
|
||||
class logic_select final : public block
|
||||
{
|
||||
public:
|
||||
struct port
|
||||
{
|
||||
enum : std::size_t
|
||||
{
|
||||
sel,
|
||||
a,
|
||||
b,
|
||||
q
|
||||
};
|
||||
};
|
||||
|
||||
explicit logic_select(block_setup const &Setup) : block(Setup) {}
|
||||
|
||||
void update(frame &Frame) override
|
||||
{
|
||||
set(Frame, port::q, get_bool(Frame, port::sel) ? get(Frame, port::b) : get(Frame, port::a));
|
||||
}
|
||||
|
||||
static block_type describe()
|
||||
{
|
||||
auto type{make_type("logic.select", "Select", "Passes input a when sel is off, and input b when sel is on.")};
|
||||
type.ports = {signal_in("sel", value_type::boolean), signal_in("a", value_type::real), signal_in("b", value_type::real), signal_out("q", value_type::real)};
|
||||
type.create = make_block<logic_select>;
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void register_logic_blocks(catalog &Catalog)
|
||||
{
|
||||
Catalog.add(logic_compare::describe());
|
||||
Catalog.add(logic_gate::describe<gate::and_>("logic.and", "AND", 1.0));
|
||||
Catalog.add(logic_gate::describe<gate::or_>("logic.or", "OR", 0.0));
|
||||
Catalog.add(logic_gate::describe<gate::xor_>("logic.xor", "XOR", 0.0));
|
||||
Catalog.add(logic_gate::describe<gate::nand_>("logic.nand", "NAND", 1.0));
|
||||
Catalog.add(logic_gate::describe<gate::nor_>("logic.nor", "NOR", 0.0));
|
||||
Catalog.add(logic_not::describe());
|
||||
Catalog.add(logic_sr::describe());
|
||||
Catalog.add(logic_timer::describe<true>());
|
||||
Catalog.add(logic_timer::describe<false>());
|
||||
Catalog.add(logic_delay::describe());
|
||||
Catalog.add(logic_hysteresis::describe());
|
||||
Catalog.add(logic_const::describe());
|
||||
Catalog.add(logic_math::describe());
|
||||
Catalog.add(logic_select::describe());
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
49
vehicle/graph/graph_block.cpp
Normal file
49
vehicle/graph/graph_block.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_block.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
block::block(block_setup const &Setup) : m_type(Setup.type), m_slots(Setup.slots) {}
|
||||
|
||||
double block::number(block_setup const &Setup, std::size_t const Param)
|
||||
{
|
||||
return Setup.params[Param].number;
|
||||
}
|
||||
|
||||
bool block::boolean(block_setup const &Setup, std::size_t const Param)
|
||||
{
|
||||
return Setup.params[Param].boolean;
|
||||
}
|
||||
|
||||
std::string const &block::text(block_setup const &Setup, std::size_t const Param)
|
||||
{
|
||||
return Setup.params[Param].text;
|
||||
}
|
||||
|
||||
int block::choice(block_setup const &Setup, std::size_t const Param)
|
||||
{
|
||||
auto const &choices{Setup.type.params[Param].choices};
|
||||
auto const &value{Setup.params[Param].text};
|
||||
for (std::size_t idx = 0; idx < choices.size(); ++idx)
|
||||
{
|
||||
if (choices[idx] == value)
|
||||
{
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
// the loader rejects values outside of the list, so this only happens with a broken catalog
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
150
vehicle/graph/graph_block.h
Normal file
150
vehicle/graph/graph_block.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_types.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
struct block_type;
|
||||
|
||||
// state of a physical connection. the output writes the forward value; every connected input adds its share
|
||||
// of the back value to back_next, which becomes visible to the output in the next step. thanks to this one step lag
|
||||
// the back channel never constrains the order of evaluation, and the result doesn't depend on the order of the inputs
|
||||
struct link_state
|
||||
{
|
||||
double forward{0.0};
|
||||
double back{0.0};
|
||||
double back_next{0.0};
|
||||
};
|
||||
|
||||
// data shared by all blocks of one graph instance during a single step
|
||||
struct frame
|
||||
{
|
||||
double dt{0.0};
|
||||
double time{0.0}; // time elapsed since the start of the instance, after this step
|
||||
bool low_voltage{false}; // graph wide switch: simulate the low voltage circuits, or treat them as always powered
|
||||
double *signals{nullptr};
|
||||
std::uint8_t const *forced{nullptr}; // signal slots with value forced from outside, writes from blocks are ignored
|
||||
link_state *links{nullptr};
|
||||
};
|
||||
|
||||
// everything a block factory needs to create a block of the given node
|
||||
struct block_setup
|
||||
{
|
||||
block_type const &type;
|
||||
std::vector<param_value> const ¶ms; // in the order of the type's parameter definitions
|
||||
std::vector<int> const &slots; // in the order of the type's port definitions; signal slot or link index
|
||||
std::vector<std::uint8_t> const &connected; // in the order of the type's port definitions
|
||||
};
|
||||
|
||||
// base of all blocks. a block reads and writes its ports through slot indices resolved when the graph was compiled
|
||||
class block
|
||||
{
|
||||
public:
|
||||
explicit block(block_setup const &Setup);
|
||||
virtual ~block() = default;
|
||||
|
||||
// called once per step in the order resolved from the connections
|
||||
virtual void update(frame &Frame) = 0;
|
||||
// called after all blocks were updated, by the blocks of types marked as deferred. a deferred block publishes its
|
||||
// outputs here, so they become visible to other blocks in the next step; this is what allows feedback loops
|
||||
virtual void commit(frame &) {}
|
||||
|
||||
// internal state shown by the inspector and written by the standalone runner, e.g. whether a lamp is lit
|
||||
virtual std::size_t state_count() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual char const *state_name(std::size_t) const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
virtual double state_value(std::size_t) const
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
block_type const &type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
protected:
|
||||
// signal ports
|
||||
double get(frame const &Frame, std::size_t Port) const
|
||||
{
|
||||
return Frame.signals[m_slots[Port]];
|
||||
}
|
||||
bool get_bool(frame const &Frame, std::size_t Port) const
|
||||
{
|
||||
return Frame.signals[m_slots[Port]] > 0.5;
|
||||
}
|
||||
void set(frame &Frame, std::size_t Port, double const Value) const
|
||||
{
|
||||
auto const slot{m_slots[Port]};
|
||||
if (Frame.forced[slot] == 0)
|
||||
{
|
||||
Frame.signals[slot] = Value;
|
||||
}
|
||||
}
|
||||
void set_bool(frame &Frame, std::size_t Port, bool const Value) const
|
||||
{
|
||||
set(Frame, Port, Value ? 1.0 : 0.0);
|
||||
}
|
||||
// physical inputs
|
||||
double forward_in(frame const &Frame, std::size_t Port) const
|
||||
{
|
||||
return Frame.links[m_slots[Port]].forward;
|
||||
}
|
||||
void add_back(frame &Frame, std::size_t Port, double const Value) const
|
||||
{
|
||||
Frame.links[m_slots[Port]].back_next += Value;
|
||||
}
|
||||
// physical outputs
|
||||
void set_forward(frame &Frame, std::size_t Port, double const Value) const
|
||||
{
|
||||
Frame.links[m_slots[Port]].forward = Value;
|
||||
}
|
||||
double back_out(frame const &Frame, std::size_t Port) const
|
||||
{
|
||||
return Frame.links[m_slots[Port]].back;
|
||||
}
|
||||
|
||||
// whether the port has a connection; known when the block is created
|
||||
static bool is_connected(block_setup const &Setup, std::size_t Port)
|
||||
{
|
||||
return Setup.connected[Port] != 0;
|
||||
}
|
||||
// parameters, read when the block is created
|
||||
static double number(block_setup const &Setup, std::size_t Param);
|
||||
static bool boolean(block_setup const &Setup, std::size_t Param);
|
||||
static std::string const &text(block_setup const &Setup, std::size_t Param);
|
||||
// index of the parameter's value in the list of choices of its definition
|
||||
static int choice(block_setup const &Setup, std::size_t Param);
|
||||
|
||||
private:
|
||||
block_type const &m_type;
|
||||
std::vector<int> m_slots;
|
||||
};
|
||||
|
||||
using block_factory = std::unique_ptr<block> (*)(block_setup const &);
|
||||
|
||||
template <class Block_> std::unique_ptr<block> make_block(block_setup const &Setup)
|
||||
{
|
||||
return std::make_unique<Block_>(Setup);
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
273
vehicle/graph/graph_catalog.cpp
Normal file
273
vehicle/graph/graph_catalog.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
std::string_view to_string(block_origin const Origin)
|
||||
{
|
||||
switch (Origin)
|
||||
{
|
||||
case block_origin::cab:
|
||||
return "cab";
|
||||
case block_origin::simulator:
|
||||
return "simulator";
|
||||
case block_origin::extracted:
|
||||
return "extracted";
|
||||
case block_origin::native:
|
||||
return "native";
|
||||
case block_origin::interface_profile:
|
||||
return "interface";
|
||||
case block_origin::script:
|
||||
return "script";
|
||||
default:
|
||||
return "primitive";
|
||||
}
|
||||
}
|
||||
|
||||
int block_type::port_index(std::string_view const Key) const
|
||||
{
|
||||
for (std::size_t idx = 0; idx < ports.size(); ++idx)
|
||||
{
|
||||
if (ports[idx].key == Key)
|
||||
{
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int block_type::param_index(std::string_view const Key) const
|
||||
{
|
||||
for (std::size_t idx = 0; idx < params.size(); ++idx)
|
||||
{
|
||||
if (params[idx].key == Key)
|
||||
{
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
catalog const &catalog::get()
|
||||
{
|
||||
static catalog const instance{[]() {
|
||||
catalog result;
|
||||
register_logic_blocks(result);
|
||||
register_cab_blocks(result);
|
||||
return result;
|
||||
}()};
|
||||
return instance;
|
||||
}
|
||||
|
||||
block_type const *catalog::find(std::string_view const Id) const
|
||||
{
|
||||
for (auto const &type : m_types)
|
||||
{
|
||||
if (type.id == Id)
|
||||
{
|
||||
return &type;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void catalog::add(block_type Type)
|
||||
{
|
||||
m_types.emplace_back(std::move(Type));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
nlohmann::json to_json(param_value const &Value)
|
||||
{
|
||||
switch (Value.type)
|
||||
{
|
||||
case param_type::boolean:
|
||||
return Value.boolean;
|
||||
case param_type::text:
|
||||
case param_type::choice:
|
||||
return Value.text;
|
||||
default:
|
||||
return Value.number;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string catalog::export_json() const
|
||||
{
|
||||
nlohmann::ordered_json root;
|
||||
root["format"] = "maszyna-vehicle-graph-catalog";
|
||||
root["version"] = 1;
|
||||
root["graph_format"] = std::string{file_format};
|
||||
root["graph_version"] = file_version;
|
||||
|
||||
auto &domains = (root["domains"] = nlohmann::ordered_json::array());
|
||||
for (std::size_t idx = 0; idx < static_cast<std::size_t>(domain::count_); ++idx)
|
||||
{
|
||||
auto const &domaininfo{info(static_cast<domain>(idx))};
|
||||
nlohmann::ordered_json item;
|
||||
item["id"] = std::string{domaininfo.id};
|
||||
item["physical"] = domaininfo.physical;
|
||||
item["forward"] = std::string{domaininfo.forward};
|
||||
item["forward_unit"] = std::string{domaininfo.forward_unit};
|
||||
if (domaininfo.physical)
|
||||
{
|
||||
item["back"] = std::string{domaininfo.back};
|
||||
item["back_unit"] = std::string{domaininfo.back_unit};
|
||||
}
|
||||
domains.push_back(std::move(item));
|
||||
}
|
||||
|
||||
auto &blocks = (root["blocks"] = nlohmann::ordered_json::array());
|
||||
for (auto const &type : m_types)
|
||||
{
|
||||
nlohmann::ordered_json item;
|
||||
item["id"] = type.id;
|
||||
item["category"] = type.category;
|
||||
item["name"] = type.name;
|
||||
item["description"] = type.description;
|
||||
item["origin"] = std::string{to_string(type.origin)};
|
||||
item["deferred"] = type.deferred;
|
||||
auto &ports = (item["ports"] = nlohmann::ordered_json::array());
|
||||
for (auto const &port : type.ports)
|
||||
{
|
||||
nlohmann::ordered_json portitem;
|
||||
portitem["key"] = port.key;
|
||||
portitem["direction"] = (port.direction == port_direction::in ? "in" : "out");
|
||||
portitem["domain"] = std::string{info(port.dom).id};
|
||||
if (port.dom == domain::signal)
|
||||
{
|
||||
portitem["type"] = std::string{to_string(port.type)};
|
||||
}
|
||||
if (false == port.unit.empty())
|
||||
{
|
||||
portitem["unit"] = port.unit;
|
||||
}
|
||||
if (port.direction == port_direction::in)
|
||||
{
|
||||
portitem["optional"] = port.optional;
|
||||
if (port.dom == domain::signal)
|
||||
{
|
||||
portitem["default"] = port.default_value;
|
||||
}
|
||||
}
|
||||
ports.push_back(std::move(portitem));
|
||||
}
|
||||
auto ¶ms = (item["params"] = nlohmann::ordered_json::array());
|
||||
for (auto const ¶m : type.params)
|
||||
{
|
||||
nlohmann::ordered_json paramitem;
|
||||
paramitem["key"] = param.key;
|
||||
paramitem["type"] = std::string{to_string(param.type)};
|
||||
if (false == param.unit.empty())
|
||||
{
|
||||
paramitem["unit"] = param.unit;
|
||||
}
|
||||
paramitem["default"] = to_json(param.default_value);
|
||||
if (param.type == param_type::choice)
|
||||
{
|
||||
paramitem["choices"] = param.choices;
|
||||
}
|
||||
params.push_back(std::move(paramitem));
|
||||
}
|
||||
blocks.push_back(std::move(item));
|
||||
}
|
||||
return root.dump(1, '\t');
|
||||
}
|
||||
|
||||
port_def signal_in(std::string Key, value_type const Type, double const Default, std::string Unit, bool const Optional)
|
||||
{
|
||||
port_def result;
|
||||
result.key = std::move(Key);
|
||||
result.direction = port_direction::in;
|
||||
result.dom = domain::signal;
|
||||
result.type = Type;
|
||||
result.unit = std::move(Unit);
|
||||
result.optional = Optional;
|
||||
result.default_value = Default;
|
||||
return result;
|
||||
}
|
||||
|
||||
port_def signal_out(std::string Key, value_type const Type, std::string Unit)
|
||||
{
|
||||
port_def result;
|
||||
result.key = std::move(Key);
|
||||
result.direction = port_direction::out;
|
||||
result.dom = domain::signal;
|
||||
result.type = Type;
|
||||
result.unit = std::move(Unit);
|
||||
return result;
|
||||
}
|
||||
|
||||
port_def physical_in(std::string Key, domain const Domain, bool const Optional)
|
||||
{
|
||||
port_def result;
|
||||
result.key = std::move(Key);
|
||||
result.direction = port_direction::in;
|
||||
result.dom = Domain;
|
||||
result.optional = Optional;
|
||||
return result;
|
||||
}
|
||||
|
||||
port_def physical_out(std::string Key, domain const Domain)
|
||||
{
|
||||
port_def result;
|
||||
result.key = std::move(Key);
|
||||
result.direction = port_direction::out;
|
||||
result.dom = Domain;
|
||||
return result;
|
||||
}
|
||||
|
||||
param_def number_param(std::string Key, double const Default, std::string Unit)
|
||||
{
|
||||
param_def result;
|
||||
result.key = std::move(Key);
|
||||
result.type = param_type::number;
|
||||
result.unit = std::move(Unit);
|
||||
result.default_value = param_value::make_number(Default);
|
||||
return result;
|
||||
}
|
||||
|
||||
param_def boolean_param(std::string Key, bool const Default)
|
||||
{
|
||||
param_def result;
|
||||
result.key = std::move(Key);
|
||||
result.type = param_type::boolean;
|
||||
result.default_value = param_value::make_boolean(Default);
|
||||
return result;
|
||||
}
|
||||
|
||||
param_def text_param(std::string Key, std::string Default)
|
||||
{
|
||||
param_def result;
|
||||
result.key = std::move(Key);
|
||||
result.type = param_type::text;
|
||||
result.default_value = param_value::make_text(std::move(Default));
|
||||
return result;
|
||||
}
|
||||
|
||||
param_def choice_param(std::string Key, std::vector<std::string> Choices, std::string Default)
|
||||
{
|
||||
param_def result;
|
||||
result.key = std::move(Key);
|
||||
result.type = param_type::choice;
|
||||
result.choices = std::move(Choices);
|
||||
result.default_value = param_value::make_choice(std::move(Default));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
90
vehicle/graph/graph_catalog.h
Normal file
90
vehicle/graph/graph_catalog.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_block.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
// where the behaviour of a block comes from; shown by the editor
|
||||
enum class block_origin : std::uint8_t
|
||||
{
|
||||
primitive, // generic building block of the graph
|
||||
cab, // cab control or indicator
|
||||
simulator, // wraps existing simulator code without changing it
|
||||
extracted, // simulator code extracted from Mover, shared with the .fiz vehicles
|
||||
native, // part of the simulator the graph connects to (vehicle body, couplers)
|
||||
interface_profile, // standard interface between vehicles, or between the vehicle and the AI
|
||||
script // lua function block
|
||||
};
|
||||
|
||||
std::string_view to_string(block_origin Origin);
|
||||
|
||||
struct block_type
|
||||
{
|
||||
std::string id; // e.g. "logic.and"; stored in the .vhg files, never change it once released
|
||||
std::string category;
|
||||
std::string name;
|
||||
std::string description;
|
||||
block_origin origin{block_origin::primitive};
|
||||
// outputs of a deferred block are published after the whole step, so connections leaving it don't take part
|
||||
// in ordering the blocks, and loops passing through it are allowed
|
||||
bool deferred{false};
|
||||
std::vector<port_def> ports;
|
||||
std::vector<param_def> params;
|
||||
block_factory create{nullptr};
|
||||
|
||||
int port_index(std::string_view Key) const;
|
||||
int param_index(std::string_view Key) const;
|
||||
};
|
||||
|
||||
// registry of all block types known to the simulator. it's also exported for the external editor
|
||||
class catalog
|
||||
{
|
||||
public:
|
||||
static catalog const &get();
|
||||
|
||||
block_type const *find(std::string_view Id) const;
|
||||
std::vector<block_type> const &types() const
|
||||
{
|
||||
return m_types;
|
||||
}
|
||||
// description of all block types and domains, for the editor
|
||||
std::string export_json() const;
|
||||
|
||||
// used by the block registration functions
|
||||
void add(block_type Type);
|
||||
|
||||
private:
|
||||
catalog() = default;
|
||||
|
||||
std::vector<block_type> m_types;
|
||||
};
|
||||
|
||||
// helpers for the definitions of block types
|
||||
port_def signal_in(std::string Key, value_type Type, double Default = 0.0, std::string Unit = "", bool Optional = false);
|
||||
port_def signal_out(std::string Key, value_type Type, std::string Unit = "");
|
||||
port_def physical_in(std::string Key, domain Domain, bool Optional = false);
|
||||
port_def physical_out(std::string Key, domain Domain);
|
||||
param_def number_param(std::string Key, double Default, std::string Unit = "");
|
||||
param_def boolean_param(std::string Key, bool Default);
|
||||
param_def text_param(std::string Key, std::string Default = "");
|
||||
param_def choice_param(std::string Key, std::vector<std::string> Choices, std::string Default);
|
||||
|
||||
// registration functions of the block families, called once when the catalog is built
|
||||
void register_logic_blocks(catalog &Catalog);
|
||||
void register_cab_blocks(catalog &Catalog);
|
||||
|
||||
} // namespace vgraph
|
||||
282
vehicle/graph/graph_format.cpp
Normal file
282
vehicle/graph/graph_format.cpp
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_format.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
void report(std::vector<diagnostic> &Diagnostics, severity const Level, std::string Node, std::string Message)
|
||||
{
|
||||
Diagnostics.push_back({Level, std::move(Node), std::move(Message)});
|
||||
}
|
||||
|
||||
bool is_valid_id(std::string const &Id)
|
||||
{
|
||||
if (Id.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return std::all_of(std::begin(Id), std::end(Id), [](char const Char) {
|
||||
return (Char >= 'a' && Char <= 'z') || (Char >= 'A' && Char <= 'Z') || (Char >= '0' && Char <= '9') || Char == '_' || Char == '-';
|
||||
});
|
||||
}
|
||||
|
||||
// reads a json scalar as a parameter value. text values become choices later, if their definition says so
|
||||
bool read_value(json const &Value, param_value &Result)
|
||||
{
|
||||
if (Value.is_boolean())
|
||||
{
|
||||
Result = param_value::make_boolean(Value.get<bool>());
|
||||
return true;
|
||||
}
|
||||
if (Value.is_number())
|
||||
{
|
||||
Result = param_value::make_number(Value.get<double>());
|
||||
return true;
|
||||
}
|
||||
if (Value.is_string())
|
||||
{
|
||||
Result = param_value::make_text(Value.get<std::string>());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void read_values(json const &Object, std::vector<named_value> &Values, std::vector<diagnostic> &Diagnostics, std::string const &Node, std::string const &What)
|
||||
{
|
||||
for (auto const &item : Object.items())
|
||||
{
|
||||
param_value value;
|
||||
if (read_value(item.value(), value))
|
||||
{
|
||||
Values.emplace_back(item.key(), std::move(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, Node, What + " \"" + item.key() + "\" has to be a number, a boolean or a text");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// splits "node.port" into its parts
|
||||
bool split_endpoint(json const &Value, std::string &Node, std::string &Port)
|
||||
{
|
||||
if (false == Value.is_string())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const text{Value.get<std::string>()};
|
||||
auto const dot{text.find('.')};
|
||||
if (dot == std::string::npos || dot == 0 || dot + 1 == text.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Node = text.substr(0, dot);
|
||||
Port = text.substr(dot + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_document_(std::string const &Text, document &Document, std::vector<diagnostic> &Diagnostics)
|
||||
{
|
||||
auto const errorcount{std::count_if(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; })};
|
||||
|
||||
// comments are allowed, to make hand editing of the files easier
|
||||
auto const root = json::parse(Text, nullptr, false, true);
|
||||
if (root.is_discarded() || false == root.is_object())
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "the file isn't a valid json object");
|
||||
return false;
|
||||
}
|
||||
if (root.value("format", std::string{}) != file_format)
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "the file isn't a vehicle graph (missing \"format\": \"" + std::string{file_format} + "\")");
|
||||
return false;
|
||||
}
|
||||
auto const version{root.value("version", 0)};
|
||||
if (version != file_version)
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "unsupported file version " + std::to_string(version) + ", expected " + std::to_string(file_version) +
|
||||
(version < file_version ? "; open and save the file in the current editor to convert it" : ""));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root.contains("vehicle") && root["vehicle"].is_object())
|
||||
{
|
||||
Document.name = root["vehicle"].value("name", "");
|
||||
}
|
||||
if (root.contains("settings"))
|
||||
{
|
||||
auto const &settings = root["settings"];
|
||||
if (settings.is_object())
|
||||
{
|
||||
for (auto const &item : settings.items())
|
||||
{
|
||||
if (item.key() == "low_voltage_simulation" && item.value().is_boolean())
|
||||
{
|
||||
Document.low_voltage_simulation = item.value().get<bool>();
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::warning, "", "unknown or malformed setting \"" + item.key() + "\" was ignored");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "\"settings\" has to be an object");
|
||||
}
|
||||
}
|
||||
if (root.contains("body"))
|
||||
{
|
||||
if (root["body"].is_object())
|
||||
{
|
||||
read_values(root["body"], Document.body, Diagnostics, "", "body parameter");
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "\"body\" has to be an object");
|
||||
}
|
||||
}
|
||||
if (root.contains("profiles"))
|
||||
{
|
||||
if (root["profiles"].is_object())
|
||||
{
|
||||
for (auto const &item : root["profiles"].items())
|
||||
{
|
||||
if (item.value().is_number_integer())
|
||||
{
|
||||
Document.profiles.emplace_back(item.key(), item.value().get<int>());
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "version of the interface profile \"" + item.key() + "\" has to be an integer");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "\"profiles\" has to be an object");
|
||||
}
|
||||
}
|
||||
|
||||
// nodes
|
||||
if (false == root.contains("nodes") || false == root["nodes"].is_array())
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "missing \"nodes\" array");
|
||||
return false;
|
||||
}
|
||||
std::set<std::string> ids;
|
||||
for (auto const &item : root["nodes"])
|
||||
{
|
||||
if (false == item.is_object())
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "every entry of \"nodes\" has to be an object");
|
||||
continue;
|
||||
}
|
||||
node_desc node;
|
||||
node.id = item.value("id", "");
|
||||
node.type = item.value("type", "");
|
||||
node.title = item.value("title", "");
|
||||
if (false == is_valid_id(node.id))
|
||||
{
|
||||
report(Diagnostics, severity::error, node.id, "node id has to be made of letters, digits, '_' and '-'");
|
||||
continue;
|
||||
}
|
||||
if (false == ids.insert(node.id).second)
|
||||
{
|
||||
report(Diagnostics, severity::error, node.id, "duplicate node id");
|
||||
continue;
|
||||
}
|
||||
if (node.type.empty())
|
||||
{
|
||||
report(Diagnostics, severity::error, node.id, "missing block type");
|
||||
continue;
|
||||
}
|
||||
if (item.contains("params"))
|
||||
{
|
||||
if (item["params"].is_object())
|
||||
{
|
||||
read_values(item["params"], node.params, Diagnostics, node.id, "parameter");
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, node.id, "\"params\" has to be an object");
|
||||
}
|
||||
}
|
||||
Document.nodes.emplace_back(std::move(node));
|
||||
}
|
||||
|
||||
// connections
|
||||
if (root.contains("connections"))
|
||||
{
|
||||
if (root["connections"].is_array())
|
||||
{
|
||||
for (auto const &item : root["connections"])
|
||||
{
|
||||
connection_desc connection;
|
||||
if (false == item.is_object() || false == split_endpoint(item.value("from", json{}), connection.from_node, connection.from_port) ||
|
||||
false == split_endpoint(item.value("to", json{}), connection.to_node, connection.to_port))
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "malformed connection " + item.dump() + ", expected {\"from\": \"node.port\", \"to\": \"node.port\"}");
|
||||
continue;
|
||||
}
|
||||
Document.connections.emplace_back(std::move(connection));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "\"connections\" has to be an array");
|
||||
}
|
||||
}
|
||||
// "layout" holds the positions of the blocks in the editor and is of no interest to the simulator
|
||||
|
||||
return errorcount == std::count_if(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool parse_document(std::string const &Text, document &Document, std::vector<diagnostic> &Diagnostics)
|
||||
{
|
||||
try
|
||||
{
|
||||
return parse_document_(Text, Document, Diagnostics);
|
||||
}
|
||||
catch (json::exception const &Error)
|
||||
{
|
||||
// values of unexpected type, e.g. a number where the id should be
|
||||
report(Diagnostics, severity::error, "", std::string{"malformed file: "} + Error.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool load_document(std::string const &Path, document &Document, std::vector<diagnostic> &Diagnostics)
|
||||
{
|
||||
std::ifstream file{Path, std::ios::binary};
|
||||
if (false == file.is_open())
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "can't open file \"" + Path + "\"");
|
||||
return false;
|
||||
}
|
||||
std::string const text{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()};
|
||||
return parse_document(text, Document, Diagnostics);
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
58
vehicle/graph/graph_format.h
Normal file
58
vehicle/graph/graph_format.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_types.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
using named_value = std::pair<std::string, param_value>;
|
||||
|
||||
struct node_desc
|
||||
{
|
||||
std::string id;
|
||||
std::string type;
|
||||
std::string title;
|
||||
std::vector<named_value> params; // as stored in the file; checked against the catalog when the graph is compiled
|
||||
};
|
||||
|
||||
struct connection_desc
|
||||
{
|
||||
std::string from_node;
|
||||
std::string from_port;
|
||||
std::string to_node;
|
||||
std::string to_port;
|
||||
};
|
||||
|
||||
// content of a .vhg file, before it's checked against the block catalog
|
||||
struct document
|
||||
{
|
||||
std::string name;
|
||||
// when enabled, blocks with low voltage ports simulate the voltages and currents of the control circuits.
|
||||
// when disabled, low voltage connections are ignored and the apparatus behaves as if always powered
|
||||
bool low_voltage_simulation{false};
|
||||
std::vector<named_value> body; // parameters of the vehicle body (mass, dimensions, axles...); used by the simulator integration
|
||||
std::vector<std::pair<std::string, int>> profiles; // standard interfaces used by the vehicle, with their versions
|
||||
std::vector<node_desc> nodes;
|
||||
std::vector<connection_desc> connections;
|
||||
};
|
||||
|
||||
// parses the content of a .vhg file. problems are appended to the diagnostics; returns false on errors
|
||||
bool parse_document(std::string const &Text, document &Document, std::vector<diagnostic> &Diagnostics);
|
||||
// reads and parses the specified .vhg file
|
||||
bool load_document(std::string const &Path, document &Document, std::vector<diagnostic> &Diagnostics);
|
||||
|
||||
} // namespace vgraph
|
||||
92
vehicle/graph/graph_instance.cpp
Normal file
92
vehicle/graph/graph_instance.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_instance.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
graph_instance::graph_instance(std::shared_ptr<graph_template const> Template)
|
||||
: m_template(std::move(Template)), m_signals(m_template->signal_defaults()), m_forced(m_signals.size(), 0), m_links(m_template->link_count())
|
||||
{
|
||||
auto const &nodes{m_template->nodes()};
|
||||
m_blocks.reserve(nodes.size());
|
||||
for (auto const &node : nodes)
|
||||
{
|
||||
block_setup const setup{*node.type, node.params, node.slots, node.connected};
|
||||
m_blocks.emplace_back(node.type->create(setup));
|
||||
}
|
||||
m_order.reserve(nodes.size());
|
||||
for (auto const nodeindex : m_template->order())
|
||||
{
|
||||
m_order.push_back(m_blocks[nodeindex].get());
|
||||
if (nodes[nodeindex].type->deferred)
|
||||
{
|
||||
m_deferred.push_back(m_blocks[nodeindex].get());
|
||||
}
|
||||
}
|
||||
// deferred blocks publish their initial state, so the blocks they feed don't see zeros in the first step
|
||||
auto frame{make_frame(0.0)};
|
||||
for (auto *item : m_deferred)
|
||||
{
|
||||
item->commit(frame);
|
||||
}
|
||||
}
|
||||
|
||||
frame graph_instance::make_frame(double const Deltatime)
|
||||
{
|
||||
frame result;
|
||||
result.dt = Deltatime;
|
||||
result.time = m_time;
|
||||
result.low_voltage = m_template->low_voltage_simulation();
|
||||
result.signals = m_signals.data();
|
||||
result.forced = m_forced.data();
|
||||
result.links = m_links.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
void graph_instance::update(double const Deltatime)
|
||||
{
|
||||
m_time += Deltatime;
|
||||
auto frame{make_frame(Deltatime)};
|
||||
for (auto *item : m_order)
|
||||
{
|
||||
item->update(frame);
|
||||
}
|
||||
for (auto *item : m_deferred)
|
||||
{
|
||||
item->commit(frame);
|
||||
}
|
||||
for (auto &link : m_links)
|
||||
{
|
||||
link.back = link.back_next;
|
||||
link.back_next = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
block *graph_instance::find_block(std::string_view const Id)
|
||||
{
|
||||
auto const nodeindex{m_template->find_node(Id)};
|
||||
return (nodeindex >= 0 ? m_blocks[nodeindex].get() : nullptr);
|
||||
}
|
||||
|
||||
void graph_instance::force(int const Slot, double const Value)
|
||||
{
|
||||
m_signals[Slot] = Value;
|
||||
m_forced[Slot] = 1;
|
||||
}
|
||||
|
||||
void graph_instance::release(int const Slot)
|
||||
{
|
||||
m_forced[Slot] = 0;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
78
vehicle/graph/graph_instance.h
Normal file
78
vehicle/graph/graph_instance.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_block.h"
|
||||
#include "vehicle/graph/graph_template.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
// simulation state of the graph of a single vehicle
|
||||
class graph_instance
|
||||
{
|
||||
public:
|
||||
explicit graph_instance(std::shared_ptr<graph_template const> Template);
|
||||
|
||||
// advances the graph by the specified time. the order of evaluation is fixed, and there's no other source
|
||||
// of time or randomness, so the same inputs always produce the same results
|
||||
void update(double Deltatime);
|
||||
|
||||
graph_template const &definition() const
|
||||
{
|
||||
return *m_template;
|
||||
}
|
||||
double time() const
|
||||
{
|
||||
return m_time;
|
||||
}
|
||||
|
||||
// block of the node with the specified index (see graph_template::nodes())
|
||||
block &node_block(std::size_t Index)
|
||||
{
|
||||
return *m_blocks[Index];
|
||||
}
|
||||
block const &node_block(std::size_t Index) const
|
||||
{
|
||||
return *m_blocks[Index];
|
||||
}
|
||||
// block of the node with the specified id, or null
|
||||
block *find_block(std::string_view Id);
|
||||
|
||||
double signal(int Slot) const
|
||||
{
|
||||
return m_signals[Slot];
|
||||
}
|
||||
// keeps the slot at the specified value regardless of what its block writes, until released
|
||||
void force(int Slot, double Value);
|
||||
void release(int Slot);
|
||||
bool is_forced(int Slot) const
|
||||
{
|
||||
return m_forced[Slot] != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
frame make_frame(double Deltatime);
|
||||
|
||||
std::shared_ptr<graph_template const> m_template;
|
||||
std::vector<std::unique_ptr<block>> m_blocks; // in the order of the template's nodes
|
||||
std::vector<block *> m_order; // blocks in the order of evaluation
|
||||
std::vector<block *> m_deferred; // blocks publishing their outputs after the step
|
||||
std::vector<double> m_signals;
|
||||
std::vector<std::uint8_t> m_forced;
|
||||
std::vector<link_state> m_links;
|
||||
double m_time{0.0};
|
||||
};
|
||||
|
||||
} // namespace vgraph
|
||||
318
vehicle/graph/graph_standalone.cpp
Normal file
318
vehicle/graph/graph_standalone.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_standalone.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
#include "vehicle/graph/graph_instance.h"
|
||||
#include "vehicle/graph/blocks/cab.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
// command line tools of the vehicle graph. they run without the rest of the simulator; the log service doesn't run
|
||||
// in this mode, so the results are reported directly to the console
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct script_event
|
||||
{
|
||||
double time{0.0};
|
||||
std::string operation;
|
||||
std::string target;
|
||||
double value{0.0};
|
||||
int line{0};
|
||||
};
|
||||
|
||||
bool load_script(std::string const &Path, std::vector<script_event> &Events)
|
||||
{
|
||||
std::ifstream file{Path};
|
||||
if (false == file.is_open())
|
||||
{
|
||||
std::printf("can't open script file \"%s\"\n", Path.c_str());
|
||||
return false;
|
||||
}
|
||||
std::string line;
|
||||
int linenumber{0};
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
++linenumber;
|
||||
auto const comment{line.find('#')};
|
||||
if (comment != std::string::npos)
|
||||
{
|
||||
line.erase(comment);
|
||||
}
|
||||
std::istringstream stream{line};
|
||||
script_event event;
|
||||
event.line = linenumber;
|
||||
if (false == static_cast<bool>(stream >> event.time))
|
||||
{
|
||||
// empty line
|
||||
continue;
|
||||
}
|
||||
if (false == static_cast<bool>(stream >> event.operation >> event.target))
|
||||
{
|
||||
std::printf("%s:%d: expected: <time> <operation> <target> [value]\n", Path.c_str(), linenumber);
|
||||
return false;
|
||||
}
|
||||
auto const needsvalue{event.operation == "set" || event.operation == "value" || event.operation == "force"};
|
||||
if (needsvalue && false == static_cast<bool>(stream >> event.value))
|
||||
{
|
||||
std::printf("%s:%d: operation \"%s\" needs a value\n", Path.c_str(), linenumber, event.operation.c_str());
|
||||
return false;
|
||||
}
|
||||
Events.push_back(event);
|
||||
}
|
||||
std::stable_sort(std::begin(Events), std::end(Events), [](script_event const &Left, script_event const &Right) { return Left.time < Right.time; });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool apply_event(graph_instance &Instance, script_event const &Event)
|
||||
{
|
||||
if (Event.operation == "force" || Event.operation == "unforce")
|
||||
{
|
||||
auto const slot{Instance.definition().find_signal(Event.target)};
|
||||
if (slot < 0)
|
||||
{
|
||||
std::printf("script line %d: unknown signal port \"%s\"\n", Event.line, Event.target.c_str());
|
||||
return false;
|
||||
}
|
||||
if (Event.operation == "force")
|
||||
{
|
||||
Instance.force(slot, Event.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.release(slot);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
auto *control{dynamic_cast<cab_switch *>(Instance.find_block(Event.target))};
|
||||
if (control == nullptr)
|
||||
{
|
||||
std::printf("script line %d: \"%s\" isn't a cab switch\n", Event.line, Event.target.c_str());
|
||||
return false;
|
||||
}
|
||||
if (Event.operation == "press")
|
||||
{
|
||||
control->press();
|
||||
}
|
||||
else if (Event.operation == "release")
|
||||
{
|
||||
control->release();
|
||||
}
|
||||
else if (Event.operation == "toggle")
|
||||
{
|
||||
control->toggle();
|
||||
}
|
||||
else if (Event.operation == "inc")
|
||||
{
|
||||
control->increase();
|
||||
}
|
||||
else if (Event.operation == "dec")
|
||||
{
|
||||
control->decrease();
|
||||
}
|
||||
else if (Event.operation == "set")
|
||||
{
|
||||
control->set_position(static_cast<int>(std::lround(Event.value)));
|
||||
}
|
||||
else if (Event.operation == "value")
|
||||
{
|
||||
control->set_value(Event.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf("script line %d: unknown operation \"%s\"\n", Event.line, Event.operation.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string format_number(double const Value)
|
||||
{
|
||||
char buffer[32];
|
||||
// negative zero is written as zero, so equal results give equal files
|
||||
std::snprintf(buffer, sizeof(buffer), "%.9g", Value == 0.0 ? 0.0 : Value);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int graph_catalog_standalone(std::string const &Output)
|
||||
{
|
||||
auto const text{catalog::get().export_json()};
|
||||
if (Output.empty())
|
||||
{
|
||||
std::printf("%s\n", text.c_str());
|
||||
return 0;
|
||||
}
|
||||
std::ofstream file{Output, std::ios::binary};
|
||||
if (false == file.is_open())
|
||||
{
|
||||
std::printf("can't create file \"%s\"\n", Output.c_str());
|
||||
return 1;
|
||||
}
|
||||
file << text << '\n';
|
||||
std::printf("block catalog written to %s (%zu block types)\n", Output.c_str(), catalog::get().types().size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int graph_run_standalone(int const Argc, char *Argv[])
|
||||
{
|
||||
// -graph-run <file.vhg> [-script <events.txt>] [-o <results.csv>] [-dt <step>] [-t <duration>] [-every <steps>]
|
||||
if (Argc < 3)
|
||||
{
|
||||
std::printf("usage: -graph-run <file.vhg> [-script <events.txt>] [-o <results.csv>] [-dt <step, s>] [-t <duration, s>] [-every <steps>]\n");
|
||||
return 1;
|
||||
}
|
||||
std::string const input{Argv[2]};
|
||||
std::string script;
|
||||
std::string output;
|
||||
double deltatime{0.01};
|
||||
double duration{0.0};
|
||||
int every{1};
|
||||
for (int idx = 3; idx + 1 < Argc; idx += 2)
|
||||
{
|
||||
std::string const option{Argv[idx]};
|
||||
std::string const value{Argv[idx + 1]};
|
||||
if (option == "-script")
|
||||
{
|
||||
script = value;
|
||||
}
|
||||
else if (option == "-o")
|
||||
{
|
||||
output = value;
|
||||
}
|
||||
else if (option == "-dt")
|
||||
{
|
||||
deltatime = std::atof(value.c_str());
|
||||
}
|
||||
else if (option == "-t")
|
||||
{
|
||||
duration = std::atof(value.c_str());
|
||||
}
|
||||
else if (option == "-every")
|
||||
{
|
||||
every = std::max(1, std::atoi(value.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::printf("unknown option \"%s\"\n", option.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (deltatime <= 0.0)
|
||||
{
|
||||
std::printf("the step has to be positive\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<diagnostic> diagnostics;
|
||||
auto const graph{load_graph(input, diagnostics)};
|
||||
for (auto const &entry : diagnostics)
|
||||
{
|
||||
std::printf("%s: %s\n", input.c_str(), to_string(entry).c_str());
|
||||
}
|
||||
if (graph == nullptr)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
std::printf("%s: %zu blocks, %zu signals, %zu links\n", input.c_str(), graph->nodes().size(), graph->signal_defaults().size(), graph->link_count());
|
||||
|
||||
std::vector<script_event> events;
|
||||
if (false == script.empty() && false == load_script(script, events))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (duration <= 0.0 && false == events.empty())
|
||||
{
|
||||
// by default run a second past the last event
|
||||
duration = events.back().time + 1.0;
|
||||
}
|
||||
if (duration <= 0.0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
graph_instance instance{graph};
|
||||
auto const signals{graph->signal_outputs()};
|
||||
std::ostringstream results;
|
||||
results << "time";
|
||||
for (auto const &signal : signals)
|
||||
{
|
||||
results << ';' << signal.first;
|
||||
}
|
||||
for (std::size_t nodeindex = 0; nodeindex < graph->nodes().size(); ++nodeindex)
|
||||
{
|
||||
auto const &item{instance.node_block(nodeindex)};
|
||||
for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex)
|
||||
{
|
||||
results << ';' << graph->nodes()[nodeindex].id << ':' << item.state_name(stateindex);
|
||||
}
|
||||
}
|
||||
results << '\n';
|
||||
|
||||
auto const stepcount{static_cast<long long>(std::ceil(duration / deltatime - 1e-9))};
|
||||
std::size_t nextevent{0};
|
||||
for (long long step = 0; step < stepcount; ++step)
|
||||
{
|
||||
// events take effect at the beginning of the first step which starts at or after their time
|
||||
auto const steptime{step * deltatime};
|
||||
while (nextevent < events.size() && events[nextevent].time <= steptime + 1e-9)
|
||||
{
|
||||
if (false == apply_event(instance, events[nextevent]))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
++nextevent;
|
||||
}
|
||||
instance.update(deltatime);
|
||||
if ((step + 1) % every != 0 && step + 1 != stepcount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
results << format_number((step + 1) * deltatime);
|
||||
for (auto const &signal : signals)
|
||||
{
|
||||
results << ';' << format_number(instance.signal(signal.second));
|
||||
}
|
||||
for (std::size_t nodeindex = 0; nodeindex < graph->nodes().size(); ++nodeindex)
|
||||
{
|
||||
auto const &item{instance.node_block(nodeindex)};
|
||||
for (std::size_t stateindex = 0; stateindex < item.state_count(); ++stateindex)
|
||||
{
|
||||
results << ';' << format_number(item.state_value(stateindex));
|
||||
}
|
||||
}
|
||||
results << '\n';
|
||||
}
|
||||
|
||||
if (output.empty())
|
||||
{
|
||||
std::printf("%s", results.str().c_str());
|
||||
return 0;
|
||||
}
|
||||
std::ofstream file{output, std::ios::binary};
|
||||
if (false == file.is_open())
|
||||
{
|
||||
std::printf("can't create file \"%s\"\n", output.c_str());
|
||||
return 1;
|
||||
}
|
||||
file << results.str();
|
||||
std::printf("%lld steps written to %s\n", stepcount, output.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
24
vehicle/graph/graph_standalone.h
Normal file
24
vehicle/graph/graph_standalone.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
// -graph-catalog [<file.json>]: writes the description of all block types, for the external editor.
|
||||
// returns: 0 on success, 1 otherwise
|
||||
int graph_catalog_standalone(std::string const &Output);
|
||||
// -graph-run <file.vhg> [options]: checks the graph and runs it without the simulator, driven by a script of events.
|
||||
// returns: 0 on success, 1 otherwise
|
||||
int graph_run_standalone(int Argc, char *Argv[]);
|
||||
|
||||
} // namespace vgraph
|
||||
417
vehicle/graph/graph_template.cpp
Normal file
417
vehicle/graph/graph_template.cpp
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_template.h"
|
||||
#include "vehicle/graph/graph_catalog.h"
|
||||
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void report(std::vector<diagnostic> &Diagnostics, severity const Level, std::string Node, std::string Message)
|
||||
{
|
||||
Diagnostics.push_back({Level, std::move(Node), std::move(Message)});
|
||||
}
|
||||
|
||||
std::string join_choices(std::vector<std::string> const &Choices)
|
||||
{
|
||||
std::string result;
|
||||
for (auto const &choice : Choices)
|
||||
{
|
||||
if (false == result.empty())
|
||||
{
|
||||
result += ", ";
|
||||
}
|
||||
result += choice;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// checks a value read from the file against the parameter definition; text values become choices here
|
||||
bool resolve_param(param_def const &Definition, param_value const &Value, param_value &Result, std::string &Problem)
|
||||
{
|
||||
switch (Definition.type)
|
||||
{
|
||||
case param_type::number:
|
||||
{
|
||||
if (Value.type != param_type::number)
|
||||
{
|
||||
Problem = "has to be a number";
|
||||
return false;
|
||||
}
|
||||
Result = Value;
|
||||
return true;
|
||||
}
|
||||
case param_type::boolean:
|
||||
{
|
||||
if (Value.type != param_type::boolean)
|
||||
{
|
||||
Problem = "has to be true or false";
|
||||
return false;
|
||||
}
|
||||
Result = Value;
|
||||
return true;
|
||||
}
|
||||
case param_type::text:
|
||||
{
|
||||
if (Value.type != param_type::text)
|
||||
{
|
||||
Problem = "has to be a text";
|
||||
return false;
|
||||
}
|
||||
Result = Value;
|
||||
return true;
|
||||
}
|
||||
case param_type::choice:
|
||||
{
|
||||
if (Value.type != param_type::text || std::find(std::begin(Definition.choices), std::end(Definition.choices), Value.text) == std::end(Definition.choices))
|
||||
{
|
||||
Problem = "has to be one of: " + join_choices(Definition.choices);
|
||||
return false;
|
||||
}
|
||||
Result = param_value::make_choice(Value.text);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Problem = "has unsupported type";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct endpoint
|
||||
{
|
||||
int node{-1};
|
||||
int port{-1};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<graph_template const> graph_template::compile(document const &Document, catalog const &Catalog, std::vector<diagnostic> &Diagnostics)
|
||||
{
|
||||
auto const errorcount{std::count_if(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; })};
|
||||
auto result{std::make_shared<graph_template>()};
|
||||
result->m_name = Document.name;
|
||||
result->m_lowvoltage = Document.low_voltage_simulation;
|
||||
result->m_body = Document.body;
|
||||
result->m_profiles = Document.profiles;
|
||||
|
||||
// nodes and their parameters
|
||||
std::unordered_map<std::string, int> nodeindices;
|
||||
std::unordered_map<std::string, bool> unresolvednodes; // nodes of unknown types, to avoid reporting their connections again
|
||||
for (auto const &desc : Document.nodes)
|
||||
{
|
||||
auto const *type{Catalog.find(desc.type)};
|
||||
if (type == nullptr)
|
||||
{
|
||||
report(Diagnostics, severity::error, desc.id, "unknown block type \"" + desc.type + "\"");
|
||||
unresolvednodes.emplace(desc.id, true);
|
||||
continue;
|
||||
}
|
||||
node_entry node;
|
||||
node.id = desc.id;
|
||||
node.title = desc.title;
|
||||
node.type = type;
|
||||
node.params.reserve(type->params.size());
|
||||
for (auto const &definition : type->params)
|
||||
{
|
||||
node.params.push_back(definition.default_value);
|
||||
}
|
||||
for (auto const ¶m : desc.params)
|
||||
{
|
||||
auto const paramindex{type->param_index(param.first)};
|
||||
if (paramindex < 0)
|
||||
{
|
||||
report(Diagnostics, severity::warning, desc.id, "unknown parameter \"" + param.first + "\" of block type \"" + type->id + "\" was ignored");
|
||||
continue;
|
||||
}
|
||||
std::string problem;
|
||||
if (false == resolve_param(type->params[paramindex], param.second, node.params[paramindex], problem))
|
||||
{
|
||||
report(Diagnostics, severity::error, desc.id, "parameter \"" + param.first + "\" " + problem);
|
||||
}
|
||||
}
|
||||
nodeindices.emplace(node.id, static_cast<int>(result->m_nodes.size()));
|
||||
result->m_nodes.emplace_back(std::move(node));
|
||||
}
|
||||
|
||||
auto &nodes{result->m_nodes};
|
||||
auto &signaldefaults{result->m_signaldefaults};
|
||||
auto &linkcount{result->m_linkcount};
|
||||
|
||||
// outputs own their slots and links
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
node.slots.assign(node.type->ports.size(), -1);
|
||||
node.connected.assign(node.type->ports.size(), 0);
|
||||
for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex)
|
||||
{
|
||||
auto const &port{node.type->ports[portindex]};
|
||||
if (port.direction != port_direction::out)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (port.dom == domain::signal)
|
||||
{
|
||||
node.slots[portindex] = static_cast<int>(signaldefaults.size());
|
||||
signaldefaults.push_back(0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.slots[portindex] = static_cast<int>(linkcount++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connections
|
||||
std::vector<std::vector<endpoint>> sources(nodes.size());
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
sources[nodeindex].resize(nodes[nodeindex].type->ports.size());
|
||||
}
|
||||
std::vector<std::vector<int>> edges(nodes.size()); // ordering constraints: source node -> consumer node
|
||||
bool lowvoltageignored{false};
|
||||
for (auto const &connection : Document.connections)
|
||||
{
|
||||
auto const label{connection.from_node + "." + connection.from_port + " -> " + connection.to_node + "." + connection.to_port};
|
||||
auto const fromlookup{nodeindices.find(connection.from_node)};
|
||||
auto const tolookup{nodeindices.find(connection.to_node)};
|
||||
if (fromlookup == nodeindices.end() || tolookup == nodeindices.end())
|
||||
{
|
||||
auto const &missing{fromlookup == nodeindices.end() ? connection.from_node : connection.to_node};
|
||||
if (unresolvednodes.find(missing) == unresolvednodes.end())
|
||||
{
|
||||
report(Diagnostics, severity::error, "", "connection " + label + " refers to unknown node \"" + missing + "\"");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto const fromnode{fromlookup->second};
|
||||
auto const tonode{tolookup->second};
|
||||
auto const &fromtype{*nodes[fromnode].type};
|
||||
auto const &totype{*nodes[tonode].type};
|
||||
auto const fromport{fromtype.port_index(connection.from_port)};
|
||||
auto const toport{totype.port_index(connection.to_port)};
|
||||
if (fromport < 0 || fromtype.ports[fromport].direction != port_direction::out)
|
||||
{
|
||||
report(Diagnostics, severity::error, connection.from_node, "connection " + label + ": \"" + connection.from_port + "\" isn't an output of block type \"" + fromtype.id + "\"");
|
||||
continue;
|
||||
}
|
||||
if (toport < 0 || totype.ports[toport].direction != port_direction::in)
|
||||
{
|
||||
report(Diagnostics, severity::error, connection.to_node, "connection " + label + ": \"" + connection.to_port + "\" isn't an input of block type \"" + totype.id + "\"");
|
||||
continue;
|
||||
}
|
||||
auto const fromdomain{fromtype.ports[fromport].dom};
|
||||
auto const todomain{totype.ports[toport].dom};
|
||||
if (fromdomain != todomain)
|
||||
{
|
||||
report(Diagnostics, severity::error, connection.to_node,
|
||||
"connection " + label + " joins different domains: " + std::string{info(fromdomain).id} + " and " + std::string{info(todomain).id});
|
||||
continue;
|
||||
}
|
||||
if (sources[tonode][toport].node >= 0)
|
||||
{
|
||||
report(Diagnostics, severity::error, connection.to_node, "input \"" + connection.to_port + "\" has more than one source; use a junction block to join them");
|
||||
continue;
|
||||
}
|
||||
if (fromdomain == domain::low_voltage && false == result->m_lowvoltage)
|
||||
{
|
||||
lowvoltageignored = true;
|
||||
}
|
||||
sources[tonode][toport] = {fromnode, fromport};
|
||||
nodes[fromnode].connected[fromport] = 1;
|
||||
nodes[tonode].connected[toport] = 1;
|
||||
if (false == fromtype.deferred)
|
||||
{
|
||||
edges[fromnode].push_back(tonode);
|
||||
}
|
||||
}
|
||||
if (lowvoltageignored)
|
||||
{
|
||||
report(Diagnostics, severity::warning, "", "low voltage connections have no effect, because low voltage simulation is disabled in the graph settings");
|
||||
}
|
||||
|
||||
// inputs read their sources, or slots of their own holding the default values
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
auto &node{nodes[nodeindex]};
|
||||
for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex)
|
||||
{
|
||||
auto const &port{node.type->ports[portindex]};
|
||||
if (port.direction != port_direction::in)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const &source{sources[nodeindex][portindex]};
|
||||
if (source.node >= 0)
|
||||
{
|
||||
node.slots[portindex] = nodes[source.node].slots[source.port];
|
||||
continue;
|
||||
}
|
||||
if (false == port.optional)
|
||||
{
|
||||
report(Diagnostics, severity::warning, node.id, "required input \"" + port.key + "\" isn't connected");
|
||||
}
|
||||
if (port.dom == domain::signal)
|
||||
{
|
||||
node.slots[portindex] = static_cast<int>(signaldefaults.size());
|
||||
signaldefaults.push_back(port.default_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
node.slots[portindex] = static_cast<int>(linkcount++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// order of evaluation. ties are resolved by the order of nodes in the file, so the result is stable
|
||||
std::vector<int> indegree(nodes.size(), 0);
|
||||
for (auto const &targets : edges)
|
||||
{
|
||||
for (auto const target : targets)
|
||||
{
|
||||
++indegree[target];
|
||||
}
|
||||
}
|
||||
std::priority_queue<int, std::vector<int>, std::greater<int>> ready;
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
if (indegree[nodeindex] == 0)
|
||||
{
|
||||
ready.push(static_cast<int>(nodeindex));
|
||||
}
|
||||
}
|
||||
while (false == ready.empty())
|
||||
{
|
||||
auto const nodeindex{ready.top()};
|
||||
ready.pop();
|
||||
result->m_order.push_back(nodeindex);
|
||||
for (auto const target : edges[nodeindex])
|
||||
{
|
||||
if (--indegree[target] == 0)
|
||||
{
|
||||
ready.push(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result->m_order.size() != nodes.size())
|
||||
{
|
||||
// strip nodes merely fed by a loop, so the report lists only the nodes the loops pass through
|
||||
std::vector<bool> remaining(nodes.size(), false);
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
remaining[nodeindex] = (indegree[nodeindex] > 0);
|
||||
}
|
||||
bool changed{true};
|
||||
while (changed)
|
||||
{
|
||||
changed = false;
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
if (false == remaining[nodeindex])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const feedsloop{std::any_of(std::begin(edges[nodeindex]), std::end(edges[nodeindex]), [&](int const Target) { return remaining[Target]; })};
|
||||
if (false == feedsloop)
|
||||
{
|
||||
remaining[nodeindex] = false;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::string loop;
|
||||
for (std::size_t nodeindex = 0; nodeindex < nodes.size(); ++nodeindex)
|
||||
{
|
||||
if (remaining[nodeindex])
|
||||
{
|
||||
loop += (loop.empty() ? "" : ", ") + nodes[nodeindex].id;
|
||||
}
|
||||
}
|
||||
report(Diagnostics, severity::error, "",
|
||||
"connections form a loop without a deferred block (e.g. logic.sr, logic.delay, logic.ton), so the order of evaluation can't be resolved; nodes in the loop: " + loop);
|
||||
}
|
||||
|
||||
auto const failed{errorcount != std::count_if(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; })};
|
||||
if (failed)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int graph_template::find_node(std::string_view const Id) const
|
||||
{
|
||||
for (std::size_t idx = 0; idx < m_nodes.size(); ++idx)
|
||||
{
|
||||
if (m_nodes[idx].id == Id)
|
||||
{
|
||||
return static_cast<int>(idx);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int graph_template::find_signal(std::string_view const Endpoint) const
|
||||
{
|
||||
auto const dot{Endpoint.find('.')};
|
||||
if (dot == std::string_view::npos)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
auto const nodeindex{find_node(Endpoint.substr(0, dot))};
|
||||
if (nodeindex < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
auto const &node{m_nodes[nodeindex]};
|
||||
auto const portindex{node.type->port_index(Endpoint.substr(dot + 1))};
|
||||
if (portindex < 0 || node.type->ports[portindex].dom != domain::signal)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return node.slots[portindex];
|
||||
}
|
||||
|
||||
std::shared_ptr<graph_template const> load_graph(std::string const &Path, std::vector<diagnostic> &Diagnostics)
|
||||
{
|
||||
document graph;
|
||||
if (false == load_document(Path, graph, Diagnostics))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return graph_template::compile(graph, catalog::get(), Diagnostics);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, int>> graph_template::signal_outputs() const
|
||||
{
|
||||
std::vector<std::pair<std::string, int>> result;
|
||||
for (auto const &node : m_nodes)
|
||||
{
|
||||
for (std::size_t portindex = 0; portindex < node.type->ports.size(); ++portindex)
|
||||
{
|
||||
auto const &port{node.type->ports[portindex]};
|
||||
if (port.direction == port_direction::out && port.dom == domain::signal)
|
||||
{
|
||||
result.emplace_back(node.id + "." + port.key, node.slots[portindex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
102
vehicle/graph/graph_template.h
Normal file
102
vehicle/graph/graph_template.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vehicle/graph/graph_format.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
struct block_type;
|
||||
class catalog;
|
||||
|
||||
// graph checked against the catalog and resolved to flat arrays: node parameters in definition order, ports bound to
|
||||
// signal slots or physical links, and the order of evaluation. it holds no simulation state and is shared by all
|
||||
// vehicles of the same type
|
||||
class graph_template
|
||||
{
|
||||
public:
|
||||
struct node_entry
|
||||
{
|
||||
std::string id;
|
||||
std::string title;
|
||||
block_type const *type{nullptr};
|
||||
std::vector<param_value> params; // in the order of the type's parameter definitions
|
||||
std::vector<int> slots; // in the order of the type's port definitions: signal slot, or link index for physical ports
|
||||
std::vector<std::uint8_t> connected; // in the order of the type's port definitions: whether the port has a connection
|
||||
};
|
||||
|
||||
// checks the document and builds the template. problems are appended to the diagnostics; returns null on errors
|
||||
static std::shared_ptr<graph_template const> compile(document const &Document, catalog const &Catalog, std::vector<diagnostic> &Diagnostics);
|
||||
|
||||
std::string const &name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
bool low_voltage_simulation() const
|
||||
{
|
||||
return m_lowvoltage;
|
||||
}
|
||||
std::vector<node_entry> const &nodes() const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
// node indices in the order of evaluation
|
||||
std::vector<int> const &order() const
|
||||
{
|
||||
return m_order;
|
||||
}
|
||||
// initial values of all signal slots; for unconnected inputs these are the values the blocks will see
|
||||
std::vector<double> const &signal_defaults() const
|
||||
{
|
||||
return m_signaldefaults;
|
||||
}
|
||||
std::size_t link_count() const
|
||||
{
|
||||
return m_linkcount;
|
||||
}
|
||||
std::vector<named_value> const &body() const
|
||||
{
|
||||
return m_body;
|
||||
}
|
||||
std::vector<std::pair<std::string, int>> const &profiles() const
|
||||
{
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
// index of the node with the specified id, or -1
|
||||
int find_node(std::string_view Id) const;
|
||||
// signal slot of the specified port, given as "node.port", or -1 if there's no such signal port
|
||||
int find_signal(std::string_view Endpoint) const;
|
||||
// names of signal outputs in "node.port" form, with their slots, in the order of nodes and ports
|
||||
std::vector<std::pair<std::string, int>> signal_outputs() const;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
bool m_lowvoltage{false};
|
||||
std::vector<node_entry> m_nodes;
|
||||
std::vector<int> m_order;
|
||||
std::vector<double> m_signaldefaults;
|
||||
std::size_t m_linkcount{0};
|
||||
std::vector<named_value> m_body;
|
||||
std::vector<std::pair<std::string, int>> m_profiles;
|
||||
};
|
||||
|
||||
// loads the specified .vhg file and compiles it against the simulator's catalog.
|
||||
// problems are appended to the diagnostics; returns null on errors
|
||||
std::shared_ptr<graph_template const> load_graph(std::string const &Path, std::vector<diagnostic> &Diagnostics);
|
||||
|
||||
} // namespace vgraph
|
||||
131
vehicle/graph/graph_types.cpp
Normal file
131
vehicle/graph/graph_types.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "vehicle/graph/graph_types.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::array<domain_info, static_cast<std::size_t>(domain::count_)> const domains{{
|
||||
{"signal", "value", "", "", "", false},
|
||||
{"high_voltage", "voltage", "V", "current", "A", true},
|
||||
{"low_voltage", "voltage", "V", "current", "A", true},
|
||||
{"aux_ac", "voltage", "V", "power", "W", true},
|
||||
{"drive", "demand", "", "power", "W", true},
|
||||
{"mechanical", "torque", "Nm", "rotational speed", "rad/s", true},
|
||||
{"traction", "force", "N", "velocity", "m/s", true},
|
||||
{"air", "pressure", "bar", "flow", "", true},
|
||||
{"brake_pipe", "pressure", "bar", "flow", "", true},
|
||||
{"feed_pipe", "pressure", "bar", "flow", "", true},
|
||||
}};
|
||||
|
||||
} // namespace
|
||||
|
||||
domain_info const &info(domain const Domain)
|
||||
{
|
||||
return domains[static_cast<std::size_t>(Domain)];
|
||||
}
|
||||
|
||||
bool parse_domain(std::string_view const Text, domain &Domain)
|
||||
{
|
||||
for (std::size_t idx = 0; idx < domains.size(); ++idx)
|
||||
{
|
||||
if (domains[idx].id == Text)
|
||||
{
|
||||
Domain = static_cast<domain>(idx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string_view to_string(value_type const Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case value_type::boolean:
|
||||
return "bool";
|
||||
case value_type::integer:
|
||||
return "int";
|
||||
default:
|
||||
return "float";
|
||||
}
|
||||
}
|
||||
|
||||
std::string_view to_string(param_type const Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case param_type::boolean:
|
||||
return "bool";
|
||||
case param_type::text:
|
||||
return "text";
|
||||
case param_type::choice:
|
||||
return "choice";
|
||||
default:
|
||||
return "number";
|
||||
}
|
||||
}
|
||||
|
||||
param_value param_value::make_number(double const Value)
|
||||
{
|
||||
param_value result;
|
||||
result.type = param_type::number;
|
||||
result.number = Value;
|
||||
return result;
|
||||
}
|
||||
|
||||
param_value param_value::make_boolean(bool const Value)
|
||||
{
|
||||
param_value result;
|
||||
result.type = param_type::boolean;
|
||||
result.boolean = Value;
|
||||
return result;
|
||||
}
|
||||
|
||||
param_value param_value::make_text(std::string Value)
|
||||
{
|
||||
param_value result;
|
||||
result.type = param_type::text;
|
||||
result.text = std::move(Value);
|
||||
return result;
|
||||
}
|
||||
|
||||
param_value param_value::make_choice(std::string Value)
|
||||
{
|
||||
param_value result;
|
||||
result.type = param_type::choice;
|
||||
result.text = std::move(Value);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool has_errors(std::vector<diagnostic> const &Diagnostics)
|
||||
{
|
||||
return std::any_of(std::begin(Diagnostics), std::end(Diagnostics), [](diagnostic const &Diagnostic) { return Diagnostic.level == severity::error; });
|
||||
}
|
||||
|
||||
std::string to_string(diagnostic const &Diagnostic)
|
||||
{
|
||||
std::string result{Diagnostic.level == severity::error ? "error" : "warning"};
|
||||
if (false == Diagnostic.node.empty())
|
||||
{
|
||||
result += " [" + Diagnostic.node + "]";
|
||||
}
|
||||
result += ": " + Diagnostic.message;
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vgraph
|
||||
133
vehicle/graph/graph_types.h
Normal file
133
vehicle/graph/graph_types.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// vehicle graph: block based description of vehicle logic and physics, loaded from .vhg files
|
||||
// in place of .fiz. the core of the graph depends only on the standard library and nlohmann json
|
||||
// (the latter only in the .cpp files), so it can be built and tested without the rest of the simulator
|
||||
namespace vgraph
|
||||
{
|
||||
|
||||
// format identification of the .vhg (vehicle graph) files
|
||||
inline constexpr std::string_view file_format{"maszyna-vehicle-graph"};
|
||||
inline constexpr int file_version{2};
|
||||
inline constexpr std::string_view file_extension{".vhg"};
|
||||
|
||||
// signal ports pass one value in one direction. physical ports pass a pair of values in opposite directions,
|
||||
// e.g. voltage from the source towards the load and current from the load back towards the source
|
||||
enum class domain : std::uint8_t
|
||||
{
|
||||
signal,
|
||||
high_voltage, // U [V] forward, I [A] back
|
||||
low_voltage, // U [V] forward, I [A] back; only simulated when the graph enables low voltage simulation
|
||||
aux_ac, // U [V] forward, P [W] back
|
||||
drive, // demand [0-1] forward, P [W] back
|
||||
mechanical, // torque [Nm] forward, rotational speed [rad/s] back
|
||||
traction, // force at the wheel rim [N] forward, velocity [m/s] back
|
||||
air, // pressure [bar] forward, flow back
|
||||
brake_pipe, // pressure [bar] forward, flow back
|
||||
feed_pipe, // pressure [bar] forward, flow back
|
||||
count_
|
||||
};
|
||||
|
||||
enum class value_type : std::uint8_t
|
||||
{
|
||||
boolean,
|
||||
integer,
|
||||
real
|
||||
};
|
||||
|
||||
enum class port_direction : std::uint8_t
|
||||
{
|
||||
in,
|
||||
out
|
||||
};
|
||||
|
||||
struct domain_info
|
||||
{
|
||||
std::string_view id;
|
||||
std::string_view forward; // quantity passed from the output towards the inputs
|
||||
std::string_view forward_unit;
|
||||
std::string_view back; // quantity passed from the inputs back towards the output
|
||||
std::string_view back_unit;
|
||||
bool physical;
|
||||
};
|
||||
|
||||
domain_info const &info(domain Domain);
|
||||
bool parse_domain(std::string_view Text, domain &Domain);
|
||||
std::string_view to_string(value_type Type);
|
||||
|
||||
enum class param_type : std::uint8_t
|
||||
{
|
||||
number,
|
||||
boolean,
|
||||
text,
|
||||
choice
|
||||
};
|
||||
|
||||
std::string_view to_string(param_type Type);
|
||||
|
||||
// value of a block parameter, as read from the file or taken from the catalog default
|
||||
struct param_value
|
||||
{
|
||||
param_type type{param_type::number};
|
||||
double number{0.0};
|
||||
bool boolean{false};
|
||||
std::string text; // also holds the choice id
|
||||
|
||||
static param_value make_number(double Value);
|
||||
static param_value make_boolean(bool Value);
|
||||
static param_value make_text(std::string Value);
|
||||
static param_value make_choice(std::string Value);
|
||||
};
|
||||
|
||||
struct port_def
|
||||
{
|
||||
std::string key;
|
||||
port_direction direction{port_direction::in};
|
||||
domain dom{domain::signal};
|
||||
value_type type{value_type::real}; // signal ports only
|
||||
std::string unit;
|
||||
bool optional{false}; // inputs only; unconnected required inputs are reported as warnings
|
||||
double default_value{0.0}; // signal inputs only; value seen by the block when the input isn't connected
|
||||
};
|
||||
|
||||
struct param_def
|
||||
{
|
||||
std::string key;
|
||||
param_type type{param_type::number};
|
||||
std::string unit;
|
||||
param_value default_value;
|
||||
std::vector<std::string> choices; // choice parameters only, stable ascii ids
|
||||
};
|
||||
|
||||
enum class severity : std::uint8_t
|
||||
{
|
||||
warning,
|
||||
error
|
||||
};
|
||||
|
||||
// problem found while loading or compiling a graph. the node is empty for problems of the whole file
|
||||
struct diagnostic
|
||||
{
|
||||
severity level{severity::error};
|
||||
std::string node;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
bool has_errors(std::vector<diagnostic> const &Diagnostics);
|
||||
std::string to_string(diagnostic const &Diagnostic);
|
||||
|
||||
} // namespace vgraph
|
||||
Reference in New Issue
Block a user