From 70abf879b31257a3b1c68d4fcd2907f8bc3d268d Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Tue, 13 Oct 2026 10:55:53 +0200 Subject: [PATCH] Add vehicle graph core: .vhg loader, logic and cab blocks, -graph-run and -graph-catalog --- CMakeLists.txt | 13 +- EU07.cpp | 17 + tools/vehicle_graph/README.md | 107 +++ .../examples/303e-logic.expected.csv | 181 +++++ .../vehicle_graph/examples/303e-logic.script | 17 + tools/vehicle_graph/examples/303e-logic.vhg | 95 +++ .../examples/vectron-logic.expected.csv | 253 +++++++ .../examples/vectron-logic.script | 17 + .../vehicle_graph/examples/vectron-logic.vhg | 112 ++++ tools/vehicle_graph/run_tests.py | 96 +++ tools/vehicle_graph/tests/bad-choice.vhg | 2 + tools/vehicle_graph/tests/bad-param-type.vhg | 2 + tools/vehicle_graph/tests/blocks.expected.csv | 36 + tools/vehicle_graph/tests/blocks.script | 16 + tools/vehicle_graph/tests/blocks.vhg | 39 ++ tools/vehicle_graph/tests/domain-mismatch.vhg | 3 + tools/vehicle_graph/tests/duplicate-id.vhg | 2 + tools/vehicle_graph/tests/loop-deferred.vhg | 15 + tools/vehicle_graph/tests/loop.vhg | 12 + tools/vehicle_graph/tests/malformed.vhg | 2 + tools/vehicle_graph/tests/two-sources.vhg | 4 + tools/vehicle_graph/tests/unconnected.vhg | 2 + tools/vehicle_graph/tests/unknown-type.vhg | 2 + tools/vehicle_graph/tests/version-1.vhg | 2 + tools/vehicle_graph/tests/wrong-direction.vhg | 3 + vehicle/graph/blocks/cab.cpp | 381 +++++++++++ vehicle/graph/blocks/cab.h | 441 ++++++++++++ vehicle/graph/blocks/logic.cpp | 625 ++++++++++++++++++ vehicle/graph/graph_block.cpp | 49 ++ vehicle/graph/graph_block.h | 150 +++++ vehicle/graph/graph_catalog.cpp | 273 ++++++++ vehicle/graph/graph_catalog.h | 90 +++ vehicle/graph/graph_format.cpp | 282 ++++++++ vehicle/graph/graph_format.h | 58 ++ vehicle/graph/graph_instance.cpp | 92 +++ vehicle/graph/graph_instance.h | 78 +++ vehicle/graph/graph_standalone.cpp | 318 +++++++++ vehicle/graph/graph_standalone.h | 24 + vehicle/graph/graph_template.cpp | 417 ++++++++++++ vehicle/graph/graph_template.h | 102 +++ vehicle/graph/graph_types.cpp | 131 ++++ vehicle/graph/graph_types.h | 133 ++++ 42 files changed, 4693 insertions(+), 1 deletion(-) create mode 100644 tools/vehicle_graph/README.md create mode 100644 tools/vehicle_graph/examples/303e-logic.expected.csv create mode 100644 tools/vehicle_graph/examples/303e-logic.script create mode 100644 tools/vehicle_graph/examples/303e-logic.vhg create mode 100644 tools/vehicle_graph/examples/vectron-logic.expected.csv create mode 100644 tools/vehicle_graph/examples/vectron-logic.script create mode 100644 tools/vehicle_graph/examples/vectron-logic.vhg create mode 100644 tools/vehicle_graph/run_tests.py create mode 100644 tools/vehicle_graph/tests/bad-choice.vhg create mode 100644 tools/vehicle_graph/tests/bad-param-type.vhg create mode 100644 tools/vehicle_graph/tests/blocks.expected.csv create mode 100644 tools/vehicle_graph/tests/blocks.script create mode 100644 tools/vehicle_graph/tests/blocks.vhg create mode 100644 tools/vehicle_graph/tests/domain-mismatch.vhg create mode 100644 tools/vehicle_graph/tests/duplicate-id.vhg create mode 100644 tools/vehicle_graph/tests/loop-deferred.vhg create mode 100644 tools/vehicle_graph/tests/loop.vhg create mode 100644 tools/vehicle_graph/tests/malformed.vhg create mode 100644 tools/vehicle_graph/tests/two-sources.vhg create mode 100644 tools/vehicle_graph/tests/unconnected.vhg create mode 100644 tools/vehicle_graph/tests/unknown-type.vhg create mode 100644 tools/vehicle_graph/tests/version-1.vhg create mode 100644 tools/vehicle_graph/tests/wrong-direction.vhg create mode 100644 vehicle/graph/blocks/cab.cpp create mode 100644 vehicle/graph/blocks/cab.h create mode 100644 vehicle/graph/blocks/logic.cpp create mode 100644 vehicle/graph/graph_block.cpp create mode 100644 vehicle/graph/graph_block.h create mode 100644 vehicle/graph/graph_catalog.cpp create mode 100644 vehicle/graph/graph_catalog.h create mode 100644 vehicle/graph/graph_format.cpp create mode 100644 vehicle/graph/graph_format.h create mode 100644 vehicle/graph/graph_instance.cpp create mode 100644 vehicle/graph/graph_instance.h create mode 100644 vehicle/graph/graph_standalone.cpp create mode 100644 vehicle/graph/graph_standalone.h create mode 100644 vehicle/graph/graph_template.cpp create mode 100644 vehicle/graph/graph_template.h create mode 100644 vehicle/graph/graph_types.cpp create mode 100644 vehicle/graph/graph_types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 62b41aa5..d8b2a3f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/EU07.cpp b/EU07.cpp index e0361829..4643088b 100644 --- a/EU07.cpp +++ b/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 #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 [-script ] [-o ] [-dt ] [-t ] [-every ] + 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 ] + 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 diff --git a/tools/vehicle_graph/README.md b/tools/vehicle_graph/README.md new file mode 100644 index 00000000..06ae25a6 --- /dev/null +++ b/tools/vehicle_graph/README.md @@ -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 ] +eu07 -graph-run [-script ] [-o ] [-dt ] [-t ] [-every ] +``` + +`-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: + +``` +# [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 `, `value `. +Operations on signal ports: `force `, `unforce `. + +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 [--update] +``` + +- `tests/*.vhg` start with `// expect: ok`, `// expect: error ` or `// expect: warning `; +- 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. diff --git a/tools/vehicle_graph/examples/303e-logic.expected.csv b/tools/vehicle_graph/examples/303e-logic.expected.csv new file mode 100644 index 00000000..451e3e65 --- /dev/null +++ b/tools/vehicle_graph/examples/303e-logic.expected.csv @@ -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 diff --git a/tools/vehicle_graph/examples/303e-logic.script b/tools/vehicle_graph/examples/303e-logic.script new file mode 100644 index 00000000..c1c2d812 --- /dev/null +++ b/tools/vehicle_graph/examples/303e-logic.script @@ -0,0 +1,17 @@ +# events for: -graph-run 303e-logic.vhg -script 303e-logic.script +# [value] +# operations on cab switches: press, release, toggle, inc, dec, set , value +# operations on signals: force , unforce +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 diff --git a/tools/vehicle_graph/examples/303e-logic.vhg b/tools/vehicle_graph/examples/303e-logic.vhg new file mode 100644 index 00000000..4975974b --- /dev/null +++ b/tools/vehicle_graph/examples/303e-logic.vhg @@ -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" } + ] +} diff --git a/tools/vehicle_graph/examples/vectron-logic.expected.csv b/tools/vehicle_graph/examples/vectron-logic.expected.csv new file mode 100644 index 00000000..a46e05ed --- /dev/null +++ b/tools/vehicle_graph/examples/vectron-logic.expected.csv @@ -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 diff --git a/tools/vehicle_graph/examples/vectron-logic.script b/tools/vehicle_graph/examples/vectron-logic.script new file mode 100644 index 00000000..0138e4ff --- /dev/null +++ b/tools/vehicle_graph/examples/vectron-logic.script @@ -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 diff --git a/tools/vehicle_graph/examples/vectron-logic.vhg b/tools/vehicle_graph/examples/vectron-logic.vhg new file mode 100644 index 00000000..d06cc7c6 --- /dev/null +++ b/tools/vehicle_graph/examples/vectron-logic.vhg @@ -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" } + ] +} diff --git a/tools/vehicle_graph/run_tests.py b/tools/vehicle_graph/run_tests.py new file mode 100644 index 00000000..c9511d04 --- /dev/null +++ b/tools/vehicle_graph/run_tests.py @@ -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 [--update] + +- every tests/*.vhg file starts with a line "// expect: ok", "// expect: error " or "// expect: warning ", + and is checked against the output of -graph-run; +- every .vhg with a .script, in both directories, is run twice, and both results have to be identical (determinism) + and equal to .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()) diff --git a/tools/vehicle_graph/tests/bad-choice.vhg b/tools/vehicle_graph/tests/bad-choice.vhg new file mode 100644 index 00000000..4c08e91d --- /dev/null +++ b/tools/vehicle_graph/tests/bad-choice.vhg @@ -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" } } ] } diff --git a/tools/vehicle_graph/tests/bad-param-type.vhg b/tools/vehicle_graph/tests/bad-param-type.vhg new file mode 100644 index 00000000..c11fb317 --- /dev/null +++ b/tools/vehicle_graph/tests/bad-param-type.vhg @@ -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" } } ] } diff --git a/tools/vehicle_graph/tests/blocks.expected.csv b/tools/vehicle_graph/tests/blocks.expected.csv new file mode 100644 index 00000000..9a8a5b44 --- /dev/null +++ b/tools/vehicle_graph/tests/blocks.expected.csv @@ -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 diff --git a/tools/vehicle_graph/tests/blocks.script b/tools/vehicle_graph/tests/blocks.script new file mode 100644 index 00000000..5a07baf8 --- /dev/null +++ b/tools/vehicle_graph/tests/blocks.script @@ -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 diff --git a/tools/vehicle_graph/tests/blocks.vhg b/tools/vehicle_graph/tests/blocks.vhg new file mode 100644 index 00000000..052992c4 --- /dev/null +++ b/tools/vehicle_graph/tests/blocks.vhg @@ -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" } + ] +} diff --git a/tools/vehicle_graph/tests/domain-mismatch.vhg b/tools/vehicle_graph/tests/domain-mismatch.vhg new file mode 100644 index 00000000..9108f479 --- /dev/null +++ b/tools/vehicle_graph/tests/domain-mismatch.vhg @@ -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" } ] } diff --git a/tools/vehicle_graph/tests/duplicate-id.vhg b/tools/vehicle_graph/tests/duplicate-id.vhg new file mode 100644 index 00000000..08055974 --- /dev/null +++ b/tools/vehicle_graph/tests/duplicate-id.vhg @@ -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" } ] } diff --git a/tools/vehicle_graph/tests/loop-deferred.vhg b/tools/vehicle_graph/tests/loop-deferred.vhg new file mode 100644 index 00000000..dc171016 --- /dev/null +++ b/tools/vehicle_graph/tests/loop-deferred.vhg @@ -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" } +] } diff --git a/tools/vehicle_graph/tests/loop.vhg b/tools/vehicle_graph/tests/loop.vhg new file mode 100644 index 00000000..58c6285a --- /dev/null +++ b/tools/vehicle_graph/tests/loop.vhg @@ -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" } +] } diff --git a/tools/vehicle_graph/tests/malformed.vhg b/tools/vehicle_graph/tests/malformed.vhg new file mode 100644 index 00000000..9cac0931 --- /dev/null +++ b/tools/vehicle_graph/tests/malformed.vhg @@ -0,0 +1,2 @@ +// expect: error malformed file +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": 5, "type": "logic.not" } ] } diff --git a/tools/vehicle_graph/tests/two-sources.vhg b/tools/vehicle_graph/tests/two-sources.vhg new file mode 100644 index 00000000..8b3d6f25 --- /dev/null +++ b/tools/vehicle_graph/tests/two-sources.vhg @@ -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" } ] } diff --git a/tools/vehicle_graph/tests/unconnected.vhg b/tools/vehicle_graph/tests/unconnected.vhg new file mode 100644 index 00000000..a069e7ac --- /dev/null +++ b/tools/vehicle_graph/tests/unconnected.vhg @@ -0,0 +1,2 @@ +// expect: warning required input "a" isn't connected +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "n", "type": "logic.not" } ] } diff --git a/tools/vehicle_graph/tests/unknown-type.vhg b/tools/vehicle_graph/tests/unknown-type.vhg new file mode 100644 index 00000000..fcb5ac36 --- /dev/null +++ b/tools/vehicle_graph/tests/unknown-type.vhg @@ -0,0 +1,2 @@ +// expect: error unknown block type +{ "format": "maszyna-vehicle-graph", "version": 2, "nodes": [ { "id": "x", "type": "el.flux_capacitor" } ] } diff --git a/tools/vehicle_graph/tests/version-1.vhg b/tools/vehicle_graph/tests/version-1.vhg new file mode 100644 index 00000000..665bbbae --- /dev/null +++ b/tools/vehicle_graph/tests/version-1.vhg @@ -0,0 +1,2 @@ +// expect: error unsupported file version 1 +{ "format": "maszyna-vehicle-graph", "version": 1, "nodes": [] } diff --git a/tools/vehicle_graph/tests/wrong-direction.vhg b/tools/vehicle_graph/tests/wrong-direction.vhg new file mode 100644 index 00000000..28cd3473 --- /dev/null +++ b/tools/vehicle_graph/tests/wrong-direction.vhg @@ -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" } ] } diff --git a/vehicle/graph/blocks/cab.cpp b/vehicle/graph/blocks/cab.cpp new file mode 100644 index 00000000..94e1d816 --- /dev/null +++ b/vehicle/graph/blocks/cab.cpp @@ -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 +#include + +namespace vgraph +{ + +namespace +{ + +std::vector split_labels(std::string const &Text) +{ + std::vector 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(choice(Setup, param::behaviour))), + m_positioncount(std::max(1, static_cast(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(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(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(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(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; + 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; + 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(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(std::clamp(get(Frame, port::value), low, high)); + if (m_twoneedles) + { + m_values[1] = static_cast(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 { return std::make_unique(Setup, true); }; + } + else + { + type.create = [](block_setup const &Setup) -> std::unique_ptr { return std::make_unique(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(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(std::max(0.0, m_basevolume * get(Frame, port::volume))); + m_pitch = static_cast(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; + 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 diff --git a/vehicle/graph/blocks/cab.h b/vehicle/graph/blocks/cab.h new file mode 100644 index 00000000..4dab7758 --- /dev/null +++ b/vehicle/graph/blocks/cab.h @@ -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 +#include + +// 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 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 diff --git a/vehicle/graph/blocks/logic.cpp b/vehicle/graph/blocks/logic.cpp new file mode 100644 index 00000000..0f75c2c7 --- /dev/null +++ b/vehicle/graph/blocks/logic.cpp @@ -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 +#include + +// 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(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; + 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 static std::unique_ptr create(block_setup const &Setup) + { + return std::make_unique(Setup, Gate_); + } + + template 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; + 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; + 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; + 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 static std::unique_ptr create(block_setup const &Setup) + { + return std::make_unique(Setup, Ondelay_); + } + + template 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; + 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; + 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; + 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; + 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(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; + 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; + return type; + } +}; + +} // namespace + +void register_logic_blocks(catalog &Catalog) +{ + Catalog.add(logic_compare::describe()); + Catalog.add(logic_gate::describe("logic.and", "AND", 1.0)); + Catalog.add(logic_gate::describe("logic.or", "OR", 0.0)); + Catalog.add(logic_gate::describe("logic.xor", "XOR", 0.0)); + Catalog.add(logic_gate::describe("logic.nand", "NAND", 1.0)); + Catalog.add(logic_gate::describe("logic.nor", "NOR", 0.0)); + Catalog.add(logic_not::describe()); + Catalog.add(logic_sr::describe()); + Catalog.add(logic_timer::describe()); + Catalog.add(logic_timer::describe()); + 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 diff --git a/vehicle/graph/graph_block.cpp b/vehicle/graph/graph_block.cpp new file mode 100644 index 00000000..5fb48f08 --- /dev/null +++ b/vehicle/graph/graph_block.cpp @@ -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(idx); + } + } + // the loader rejects values outside of the list, so this only happens with a broken catalog + return 0; +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_block.h b/vehicle/graph/graph_block.h new file mode 100644 index 00000000..c0e85ba8 --- /dev/null +++ b/vehicle/graph/graph_block.h @@ -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 +#include +#include + +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 const ¶ms; // in the order of the type's parameter definitions + std::vector const &slots; // in the order of the type's port definitions; signal slot or link index + std::vector 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 m_slots; +}; + +using block_factory = std::unique_ptr (*)(block_setup const &); + +template std::unique_ptr make_block(block_setup const &Setup) +{ + return std::make_unique(Setup); +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_catalog.cpp b/vehicle/graph/graph_catalog.cpp new file mode 100644 index 00000000..7bf6b406 --- /dev/null +++ b/vehicle/graph/graph_catalog.cpp @@ -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 + +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(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(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(domain::count_); ++idx) + { + auto const &domaininfo{info(static_cast(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 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 diff --git a/vehicle/graph/graph_catalog.h b/vehicle/graph/graph_catalog.h new file mode 100644 index 00000000..03e31ee0 --- /dev/null +++ b/vehicle/graph/graph_catalog.h @@ -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 +#include +#include + +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 ports; + std::vector 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 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 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 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 diff --git a/vehicle/graph/graph_format.cpp b/vehicle/graph/graph_format.cpp new file mode 100644 index 00000000..c037413b --- /dev/null +++ b/vehicle/graph/graph_format.cpp @@ -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 +#include +#include +#include + +namespace vgraph +{ + +namespace +{ + +using json = nlohmann::json; + +void report(std::vector &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()); + return true; + } + if (Value.is_number()) + { + Result = param_value::make_number(Value.get()); + return true; + } + if (Value.is_string()) + { + Result = param_value::make_text(Value.get()); + return true; + } + return false; +} + +void read_values(json const &Object, std::vector &Values, std::vector &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()}; + 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 &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(); + } + 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()); + } + 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 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 &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 &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(file), std::istreambuf_iterator()}; + return parse_document(text, Document, Diagnostics); +} + +} // namespace vgraph diff --git a/vehicle/graph/graph_format.h b/vehicle/graph/graph_format.h new file mode 100644 index 00000000..b781780f --- /dev/null +++ b/vehicle/graph/graph_format.h @@ -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 +#include +#include +#include + +namespace vgraph +{ + +using named_value = std::pair; + +struct node_desc +{ + std::string id; + std::string type; + std::string title; + std::vector 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 body; // parameters of the vehicle body (mass, dimensions, axles...); used by the simulator integration + std::vector> profiles; // standard interfaces used by the vehicle, with their versions + std::vector nodes; + std::vector 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 &Diagnostics); +// reads and parses the specified .vhg file +bool load_document(std::string const &Path, document &Document, std::vector &Diagnostics); + +} // namespace vgraph diff --git a/vehicle/graph/graph_instance.cpp b/vehicle/graph/graph_instance.cpp new file mode 100644 index 00000000..5d70789d --- /dev/null +++ b/vehicle/graph/graph_instance.cpp @@ -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 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 diff --git a/vehicle/graph/graph_instance.h b/vehicle/graph/graph_instance.h new file mode 100644 index 00000000..18b39445 --- /dev/null +++ b/vehicle/graph/graph_instance.h @@ -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 +#include +#include + +namespace vgraph +{ + +// simulation state of the graph of a single vehicle +class graph_instance +{ +public: + explicit graph_instance(std::shared_ptr 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 m_template; + std::vector> m_blocks; // in the order of the template's nodes + std::vector m_order; // blocks in the order of evaluation + std::vector m_deferred; // blocks publishing their outputs after the step + std::vector m_signals; + std::vector m_forced; + std::vector m_links; + double m_time{0.0}; +}; + +} // namespace vgraph diff --git a/vehicle/graph/graph_standalone.cpp b/vehicle/graph/graph_standalone.cpp new file mode 100644 index 00000000..063424e1 --- /dev/null +++ b/vehicle/graph/graph_standalone.cpp @@ -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 +#include +#include +#include + +// 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 &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(stream >> event.time)) + { + // empty line + continue; + } + if (false == static_cast(stream >> event.operation >> event.target)) + { + std::printf("%s:%d: expected: