From d1d25b69e2112b6554135bc7ef1984112923134b Mon Sep 17 00:00:00 2001 From: julianharbarth Date: Thu, 12 Jan 2023 18:32:28 +0100 Subject: [PATCH 01/51] exclusion wip --- .../infrastructure/interlocking/exclusion.h | 5 ++ src/infrastructure/interlocking/exclusion.cc | 86 +++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/include/soro/infrastructure/interlocking/exclusion.h b/include/soro/infrastructure/interlocking/exclusion.h index 45de8d49..4fb4f610 100644 --- a/include/soro/infrastructure/interlocking/exclusion.h +++ b/include/soro/infrastructure/interlocking/exclusion.h @@ -6,6 +6,11 @@ namespace soro::infra { +using exclusion_set_id = uint32_t; + +soro::vector> +get_interlocking_route_exclusion_sets(infrastructure_t const& infra); + /* * Exclusion paths are used to partition the infrastructure into * paths that can be used to compose station routes as well as interlocking diff --git a/src/infrastructure/interlocking/exclusion.cc b/src/infrastructure/interlocking/exclusion.cc index 9e214caf..3c193955 100644 --- a/src/infrastructure/interlocking/exclusion.cc +++ b/src/infrastructure/interlocking/exclusion.cc @@ -3,11 +3,14 @@ #include "utl/concat.h" #include "utl/enumerate.h" #include "utl/erase_duplicates.h" +#include "utl/erase_if.h" #include "utl/logging.h" #include "utl/pipes.h" #include "utl/timer.h" #include "utl/to_vec.h" +#include "cista/containers/bitvec.h" + #include "soro/utls/algo/overlap.h" #include "soro/utls/coroutine/collect.h" #include "soro/utls/coroutine/coro_map.h" @@ -403,4 +406,87 @@ get_interlocking_route_exclusions(interlocking_subsystem const&, return {}; } +struct exclusion_set { + using offset_t = soro::size_t; + static constexpr auto INVALID_OFFSET = std::numeric_limits::max(); + + exclusion_set() = default; + + template + exclusion_set(std::vector const& sorted_ids) { + utls::sassert(utls::is_sorted(sorted_ids), "IDs not sorted."); + + if (sorted_ids.empty()) { + return; + } + + first_ = sorted_ids.front(); + last_ = sorted_ids.back() + 1; + bits_.resize(last_ - first_); + + for (auto const id : sorted_ids) { + bits_.set(id - first_); + } + } + + bool subset(exclusion_set const& other) const { + std::ignore = other; + return false; + } + + bool superset(exclusion_set const& other) const { + std::ignore = other; + return false; + } + + void clear() { + first_ = INVALID_OFFSET; + last_ = INVALID_OFFSET; + bits_ = {}; + } + + bool empty() const { + return first_ == INVALID_OFFSET && last_ == INVALID_OFFSET && bits_.empty(); + } + + offset_t first_{INVALID_OFFSET}; + offset_t last_{INVALID_OFFSET}; + + cista::offset::bitvec bits_{}; +}; + +soro::vector get_interlocking_exclusion_sets( + infrastructure_t const& infra) { + static const type_set POI_TYPES = {type::SIMPLE_SWITCH, type::CROSS}; + + soro::vector> expanded_sets( + infra.graph_.elements_.size()); + + for (auto const& ir : infra.interlocking_.routes_) { + for (auto const& rn : ir.iterate(infrastructure(&infra))) { + if (POI_TYPES.contains(rn.node_->type())) { + expanded_sets[rn.node_->element_->id()].emplace_back(ir.id_); + } + } + } + + for (auto& set : expanded_sets) { + utls::sort(set); + } + + auto sets = + soro::to_vec(expanded_sets, [](auto&& s) { return exclusion_set(s); }); + + utl::erase_if(sets, [](auto&& s) { return s.empty(); }); + + return sets; +} + +soro::vector> +get_interlocking_route_exclusion_sets(infrastructure_t const& infra) { + soro::vector> exclusion_sets; + + return exclusion_sets; +} + } // namespace soro::infra From 5ad941134ecb28432a21f8c564047a6699a85c18 Mon Sep 17 00:00:00 2001 From: julianharbarth Date: Fri, 20 Jan 2023 11:50:16 +0100 Subject: [PATCH 02/51] exclusion wip --- .../infrastructure/interlocking/exclusion.h | 5 - .../infrastructure/interlocking/exclusion2.h | 98 +++++++ src/infrastructure/interlocking/exclusion.cc | 83 ------ src/infrastructure/interlocking/exclusion2.cc | 275 ++++++++++++++++++ .../get_interlocking_subsystem.cc | 10 + .../interlocking_exclusion_test.cc | 46 +++ 6 files changed, 429 insertions(+), 88 deletions(-) create mode 100644 include/soro/infrastructure/interlocking/exclusion2.h create mode 100644 src/infrastructure/interlocking/exclusion2.cc create mode 100644 test/src/infrastructure/interlocking_exclusion_test.cc diff --git a/include/soro/infrastructure/interlocking/exclusion.h b/include/soro/infrastructure/interlocking/exclusion.h index 4fb4f610..45de8d49 100644 --- a/include/soro/infrastructure/interlocking/exclusion.h +++ b/include/soro/infrastructure/interlocking/exclusion.h @@ -6,11 +6,6 @@ namespace soro::infra { -using exclusion_set_id = uint32_t; - -soro::vector> -get_interlocking_route_exclusion_sets(infrastructure_t const& infra); - /* * Exclusion paths are used to partition the infrastructure into * paths that can be used to compose station routes as well as interlocking diff --git a/include/soro/infrastructure/interlocking/exclusion2.h b/include/soro/infrastructure/interlocking/exclusion2.h new file mode 100644 index 00000000..9d27472b --- /dev/null +++ b/include/soro/infrastructure/interlocking/exclusion2.h @@ -0,0 +1,98 @@ +#pragma once + +#include "soro/infrastructure/infrastructure.h" + +namespace soro::infra { + +struct exclusion_set { + using id = uint32_t; + using offset_t = soro::size_t; + using bitvec_t = cista::raw::bitvec; + + static constexpr auto INVALID_OFFSET = std::numeric_limits::max(); + + exclusion_set() = default; + + template + exclusion_set(std::vector const& sorted_ids) { + utls::sassert(utls::is_sorted(sorted_ids), "IDs not sorted."); + + if (sorted_ids.empty()) { + return; + } + + first_ = sorted_ids.front(); + last_ = sorted_ids.back() + 1; + bits_.resize(static_cast(last_ - first_)); + + for (auto const id : sorted_ids) { + bits_.set(static_cast(id - first_)); + } + } + + void set_first(offset_t const new_first) { + if (first_ == new_first) { + return; + } + + utls::sassert(new_first < first_); + + this->bits_.resize(bits_.size() + first_ - new_first); + this->bits_ <<= first_ - new_first; + + this->first_ = new_first; + } + + void set_last(offset_t const new_last) { + if (last_ == new_last) { + return; + } + + utls::sassert(new_last > last_); + this->bits_.resize(bits_.size() + new_last - last_); + this->last_ = new_last; + } + + bool contains(exclusion_set const& other) const { + if (this->empty()) { + return false; + } + + if (other.empty()) { + return true; + } + + utls::sassert(first_ == other.first_ && last_ == other.last_ && + bits_.size() == other.bits_.size()); + + for (auto i = 0U; i < bits_.size(); ++i) { + if (!this->bits_[i] && other.bits_[i]) { + return false; + } + } + + return true; + } + + bool operator==(exclusion_set const& o) const noexcept = default; + + void clear() { + first_ = INVALID_OFFSET; + last_ = INVALID_OFFSET; + bits_ = {}; + } + + bool empty() const { + return first_ == INVALID_OFFSET && last_ == INVALID_OFFSET && bits_.empty(); + } + + offset_t first_{INVALID_OFFSET}; + offset_t last_{INVALID_OFFSET}; + + bitvec_t bits_{}; +}; + +soro::vector get_interlocking_exclusion_sets( + infrastructure const& infra); + +} // namespace soro::infra diff --git a/src/infrastructure/interlocking/exclusion.cc b/src/infrastructure/interlocking/exclusion.cc index 3c193955..0c0807db 100644 --- a/src/infrastructure/interlocking/exclusion.cc +++ b/src/infrastructure/interlocking/exclusion.cc @@ -406,87 +406,4 @@ get_interlocking_route_exclusions(interlocking_subsystem const&, return {}; } -struct exclusion_set { - using offset_t = soro::size_t; - static constexpr auto INVALID_OFFSET = std::numeric_limits::max(); - - exclusion_set() = default; - - template - exclusion_set(std::vector const& sorted_ids) { - utls::sassert(utls::is_sorted(sorted_ids), "IDs not sorted."); - - if (sorted_ids.empty()) { - return; - } - - first_ = sorted_ids.front(); - last_ = sorted_ids.back() + 1; - bits_.resize(last_ - first_); - - for (auto const id : sorted_ids) { - bits_.set(id - first_); - } - } - - bool subset(exclusion_set const& other) const { - std::ignore = other; - return false; - } - - bool superset(exclusion_set const& other) const { - std::ignore = other; - return false; - } - - void clear() { - first_ = INVALID_OFFSET; - last_ = INVALID_OFFSET; - bits_ = {}; - } - - bool empty() const { - return first_ == INVALID_OFFSET && last_ == INVALID_OFFSET && bits_.empty(); - } - - offset_t first_{INVALID_OFFSET}; - offset_t last_{INVALID_OFFSET}; - - cista::offset::bitvec bits_{}; -}; - -soro::vector get_interlocking_exclusion_sets( - infrastructure_t const& infra) { - static const type_set POI_TYPES = {type::SIMPLE_SWITCH, type::CROSS}; - - soro::vector> expanded_sets( - infra.graph_.elements_.size()); - - for (auto const& ir : infra.interlocking_.routes_) { - for (auto const& rn : ir.iterate(infrastructure(&infra))) { - if (POI_TYPES.contains(rn.node_->type())) { - expanded_sets[rn.node_->element_->id()].emplace_back(ir.id_); - } - } - } - - for (auto& set : expanded_sets) { - utls::sort(set); - } - - auto sets = - soro::to_vec(expanded_sets, [](auto&& s) { return exclusion_set(s); }); - - utl::erase_if(sets, [](auto&& s) { return s.empty(); }); - - return sets; -} - -soro::vector> -get_interlocking_route_exclusion_sets(infrastructure_t const& infra) { - soro::vector> exclusion_sets; - - return exclusion_sets; -} - } // namespace soro::infra diff --git a/src/infrastructure/interlocking/exclusion2.cc b/src/infrastructure/interlocking/exclusion2.cc new file mode 100644 index 00000000..bda2cc4d --- /dev/null +++ b/src/infrastructure/interlocking/exclusion2.cc @@ -0,0 +1,275 @@ +#include "soro/infrastructure/interlocking/exclusion.h" + +#include "utl/concat.h" +#include "utl/enumerate.h" +#include "utl/erase_duplicates.h" +#include "utl/erase_if.h" +#include "utl/logging.h" +#include "utl/pipes.h" +#include "utl/timer.h" +#include "utl/to_vec.h" + +#include "cista/containers/bitvec.h" + +#include "soro/utls/algo/overlap.h" +#include "soro/utls/coroutine/collect.h" +#include "soro/utls/coroutine/coro_map.h" + +#include "soro/infrastructure/interlocking/exclusion2.h" + +namespace soro::infra { + +static const type_set POI_TYPES = { + type::SIMPLE_SWITCH, type::CROSS, type::MAIN_SIGNAL, type::HALT, + type::BORDER, type::BUMPER, type::TRACK_END}; + +struct exclusion_materials { + explicit exclusion_materials(infrastructure const& infra) + : element_used_by_{infra->graph_.elements_.size()}, + working_sets_{infra->graph_.elements_.size()}, + path_working_sets_{infra->interlocking_.routes_.size()} {} + + // size = #elements + // every ir that uses the given POI element + std::vector> element_used_by_; + + // size = #elements + // every POI element that is reachable from the given POI element + std::vector> working_sets_; + + // size = #IRs + // every POI element that is reacheable by a single IR + std::vector> path_working_sets_; +}; + +exclusion_materials get_exclusion_materials(infrastructure const& infra) { + utl::scoped_timer const t("Constructing exclusion materials used by IRs"); + + exclusion_materials mats(infra); + + for (auto const& [ir_source_node, starting_at] : + utl::enumerate(infra->interlocking_.starting_at_)) { + + if (starting_at.empty()) { + continue; + } + + for (auto const ir_id : starting_at) { + + auto const& ir = infra->interlocking_.routes_[ir_id]; + + // assert + // if (ir_source_node != ir.first_node(infra)->id_) { + // std::cout << "error\n"; + // } + + for (auto const& rn : ir.iterate(infra)) { + if (!POI_TYPES.contains(rn.node_->type())) { + continue; + } + + // TODO(julian) this only adds the elements in one direction, + // missing the other direction for now + mats.element_used_by_[rn.node_->element_->id()].emplace_back(ir.id_); + mats.working_sets_[ir.first_node(infra)->element_->id()].emplace_back( + rn.node_->element_->id()); + mats.path_working_sets_[ir.id_].emplace_back(rn.node_->element_->id()); + } + } + } + + for (auto& v : mats.element_used_by_) { + utl::erase_duplicates(v); + } + + for (auto& v : mats.working_sets_) { + utl::erase_duplicates(v); + } + + for (auto& v : mats.path_working_sets_) { + utl::erase_duplicates(v); + } + + // auto expanded_working_sets = mats.working_sets_; + // for (auto const& working_set : mats.working_sets_) { + // for (auto const&kk) + // + // } + + return mats; +} + +void clean_up_working_sets( + soro::vector>& working_sets) { + + soro::size_t counter = 0; + for (auto& sets : working_sets) { + if (sets.empty()) { + continue; + } + + ++counter; + + if (counter % 1000 == 0) { + std::cout << counter << std::endl; + } + + auto min_first = sets.front().first_; + auto max_last = sets.front().last_; + for (auto const& set : sets) { + min_first = std::min(min_first, set.first_); + max_last = std::max(max_last, set.last_); + } + + for (auto& set : sets) { + set.set_first(min_first); + set.set_last(max_last); + } + + for (auto i = 0U; i < sets.size(); ++i) { + for (auto j = 0U; j < sets.size(); ++j) { + if (sets[i].empty() || sets[j].empty() || i == j) { + continue; + } + + if (sets[i].contains(sets[j])) { + sets[j].clear(); + } + } + } + + utl::erase_if(sets, [](auto&& s) { return s.empty(); }); + } +} + +soro::vector get_interlocking_exclusion_sets( + infrastructure const& infra) { + + // auto const element_used_by = get_element_used_by(infra); + + auto const exclusion_mats = get_exclusion_materials(infra); + + auto sets = soro::to_vec(exclusion_mats.element_used_by_, + [](auto&& s) { return exclusion_set(s); }); + + std::cout << "sets done" << std::endl; + + auto working_sets = + soro::to_vec(exclusion_mats.working_sets_, [&](auto&& ws) { + return soro::to_vec(ws, [&](auto&& e_id) { return sets[e_id]; }); + }); + + std::cout << "working sets done" << std::endl; + + { + std::size_t non_empty = 0; + std::size_t size = 0; + std::vector sizes; + for (auto const& set : working_sets) { + if (set.empty()) { + continue; + } + + ++non_empty; + size += set.size(); + sizes.emplace_back(set.size()); + } + + std::sort(std::begin(sizes), std::end(sizes)); + + std::cout << "working sets before\n"; + std::cout << "non empty: " << non_empty << '\n'; + std::cout << "avg diff: " << size / non_empty << '\n'; + std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; + std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; + std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; + std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; + std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + } + + clean_up_working_sets(working_sets); + + { + std::size_t non_empty = 0; + std::size_t size = 0; + std::vector sizes; + for (auto const& set : working_sets) { + if (set.empty()) { + continue; + } + + ++non_empty; + size += set.size(); + sizes.emplace_back(set.size()); + } + + std::sort(std::begin(sizes), std::end(sizes)); + + std::cout << "working sets after\n"; + std::cout << "non empty: " << non_empty << '\n'; + std::cout << "avg diff: " << size / non_empty << '\n'; + std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; + std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; + std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; + std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; + std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + } + + { + std::size_t non_empty = 0; + std::size_t size = 0; + std::vector sizes; + for (auto const& set : exclusion_mats.working_sets_) { + if (set.empty()) { + continue; + } + + ++non_empty; + size += set.size(); + sizes.emplace_back(set.size()); + } + + std::sort(std::begin(sizes), std::end(sizes)); + + std::cout << "working sets\n"; + std::cout << "non empty: " << non_empty << '\n'; + std::cout << "avg diff: " << size / non_empty << '\n'; + std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; + std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; + std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; + std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; + std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + } + + { + std::size_t non_empty = 0; + std::size_t size = 0; + std::vector sizes; + for (auto const& set : exclusion_mats.path_working_sets_) { + if (set.empty()) { + continue; + } + + ++non_empty; + size += set.size(); + sizes.emplace_back(set.size()); + } + + std::sort(std::begin(sizes), std::end(sizes)); + + std::cout << "path working sets\n"; + std::cout << "non empty: " << non_empty << '\n'; + std::cout << "avg diff: " << size / non_empty << '\n'; + std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; + std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; + std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; + std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; + std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + } + + // utl::erase_if(sets, [](auto&& s) { return s.empty(); }); + + return sets; +} + +} // namespace soro::infra diff --git a/src/infrastructure/interlocking/get_interlocking_subsystem.cc b/src/infrastructure/interlocking/get_interlocking_subsystem.cc index c9498c91..a84eea78 100644 --- a/src/infrastructure/interlocking/get_interlocking_subsystem.cc +++ b/src/infrastructure/interlocking/get_interlocking_subsystem.cc @@ -72,6 +72,16 @@ auto get_starting_at( utls::sort(v); } + utls::sasserts([&]() { + auto const acc = + utls::accumulate(starting_at, soro::size_t{0}, + [](auto&& acc, auto&& v) { return acc + v.size(); }); + utls::sassert( + acc == interlocking_routes.size(), + "Total interlocking routes {}, but accumulated starting at {}.", + interlocking_routes.size(), acc); + }); + return starting_at; } diff --git a/test/src/infrastructure/interlocking_exclusion_test.cc b/test/src/infrastructure/interlocking_exclusion_test.cc new file mode 100644 index 00000000..1d0a9a70 --- /dev/null +++ b/test/src/infrastructure/interlocking_exclusion_test.cc @@ -0,0 +1,46 @@ +#include "doctest/doctest.h" + +#include "test/file_paths.h" + +#include "soro/infrastructure/interlocking/exclusion2.h" + +using namespace soro; +using namespace soro::infra; + +TEST_SUITE("interlocking exclusion") { + TEST_CASE("t") { + auto opts = test::DE_ISS_OPTS; + // auto opts = test::SMALL_OPTS; + opts.determine_conflicts_ = false; + opts.determine_interlocking_ = true; + opts.determine_layout_ = false; + + infrastructure infra(opts); + + auto const sets = get_interlocking_exclusion_sets(infra); + + std::size_t non_empty = 0; + std::size_t diff = 0; + std::vector diffs; + for (auto const& set : sets) { + if (set.empty()) { + continue; + } + + ++non_empty; + diff += set.last_ - set.first_; + diffs.emplace_back(set.last_ - set.first_); + } + + std::sort(std::begin(diffs), std::end(diffs)); + + std::cout << "exclusion sets\n"; + std::cout << "non empty: " << non_empty << '\n'; + std::cout << "avg diff: " << diff / non_empty << '\n'; + std::cout << "25%: " << diffs[diffs.size() * 0.25] << '\n'; + std::cout << "50%: " << diffs[diffs.size() * 0.5] << '\n'; + std::cout << "75%: " << diffs[diffs.size() * 0.75] << '\n'; + std::cout << "90%: " << diffs[diffs.size() * 0.9] << '\n'; + std::cout << "99%: " << diffs[diffs.size() * 0.99] << '\n'; + } +} From 0616e3dc6676103bda83a6c6601c3afdd6913c11 Mon Sep 17 00:00:00 2001 From: Leon Vack Date: Tue, 24 Jan 2023 14:30:41 +0100 Subject: [PATCH 03/51] Story 8: Make UI Tests fail when a JavaScript error occurs (#43) * add a basic ui test with github actions intergraion * also run un-tests using firefox * make ui-tests fail on JS errors --- .github/workflows/linux.yml | 69 ++++++++++++++++++++- ui-test/basic-test.py | 117 ++++++++++++++++++++++++++++++++++++ ui-test/default.nix | 81 +++++++++++++++++++++++++ ui-test/docker-compose.yml | 34 +++++++++++ ui-test/requirements.txt | 1 + 5 files changed, 300 insertions(+), 2 deletions(-) create mode 100755 ui-test/basic-test.py create mode 100644 ui-test/default.nix create mode 100644 ui-test/docker-compose.yml create mode 100644 ui-test/requirements.txt diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2efccab3..8df04f28 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -23,12 +23,12 @@ jobs: config: - preset: clang-release artifact: linux-amd64 + for_ui_tests: true - preset: clang-debug - preset: gcc-release - preset: gcc-debug - preset: clang-tidy - preset: clang-sanitizer - env: BUILDCACHE_DIR: /buildcache BUILDCACHE_DIRECT_MODE: true @@ -62,6 +62,27 @@ jobs: cmake --build build/${{ matrix.config.preset }} --target soro-server buildcache -s + - name: Prepare upload soro-server + if: matrix.config.for_ui_tests + run: | + mkdir server-files + cp -r ./build/${{ matrix.config.preset }}/soro-server server-files/ + cp -r ./build/${{ matrix.config.preset }}/server_resources server-files/ + cp -r resources server-files/ + + - name: Upload soro-server + if: matrix.config.for_ui_tests + uses: actions/upload-artifact@v3 + with: + name: soro-server + path: | + server-files/** + + - name: Cleanup upload soro-server + if: matrix.config.for_ui_tests + run: | + rm -rf server-files + # ==== WEB TESTS ==== - name: Run Server run: | @@ -112,7 +133,6 @@ jobs: uses: docker/setup-buildx-action@v2 with: install: true - - name: Docker Login to GitHub Container Registry uses: docker/login-action@v2 with: @@ -142,3 +162,48 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64 + + uitests: + needs: linux + runs-on: ubuntu-latest + services: + selenium-hub: + image: selenium/hub:4.7.2-20221219 + ports: + - 4442:4442 + - 4443:4443 + - 4444:4444 + chrome: + image: selenium/node-chrome:4.7.2-20221219 + options: --shm-size="2g" --add-host host.docker.internal:host-gateway + env: + SE_EVENT_BUS_HOST: selenium-hub + SE_EVENT_BUS_PUBLISH_PORT: 4442 + SE_EVENT_BUS_SUBSCRIBE_PORT: 4443 + firefox: + image: selenium/node-firefox:4.7.2-20221219 + options: --shm-size="2g" --add-host host.docker.internal:host-gateway + env: + SE_EVENT_BUS_HOST: selenium-hub + SE_EVENT_BUS_PUBLISH_PORT: 4442 + SE_EVENT_BUS_SUBSCRIBE_PORT: 4443 + steps: + - uses: actions/checkout@v3 + - name: Cache python dependencies + uses: actions/setup-python@v4 + with: + python-version: '3.9' + cache: 'pip' + - name: Install python dependencies + run: pip install -r ui-test/requirements.txt + - name: Download soro-server + uses: actions/download-artifact@v3 + with: + name: soro-server + - name: Prepare soro-server + run: chmod +x soro-server + - name: Check hostname + run: hostname + - name: Run UI tests + run: | + find ui-test -type f -executable -name '*.py' -print0 | xargs -0 -n1 python3 diff --git a/ui-test/basic-test.py b/ui-test/basic-test.py new file mode 100755 index 00000000..902fab6d --- /dev/null +++ b/ui-test/basic-test.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 + +import unittest +import subprocess + +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.wait import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC + +target = "http://host.docker.internal:8080/" + + +class SeleniumTestCase(unittest.TestCase): + """A base class for Selenium cases. + + Handles common set up and tear down. + """ + + def setUp(self): + """Set up Selenium test resources. + + Creates the Selenium driver connection and soro server process + """ + opts = webdriver.ChromeOptions() + chromeDriver = webdriver.Remote( + command_executor="http://localhost:4444/wd/hub", + options=opts, keep_alive=True + ) + + opts = webdriver.FirefoxOptions() + firefoxDriver = webdriver.Remote( + command_executor="http://localhost:4444/wd/hub", + options=opts, keep_alive=True + ) + + self.drivers = [chromeDriver, firefoxDriver] + + self.proc = subprocess.Popen([ + "./soro-server", "--port", "8080", + "--resource_dir", "resources", + "--server_resource_dir", "server_resources" + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + for line in self.proc.stderr: + if b"tiles-server started on 0.0.0.0:" in line: + break + + def tearDown(self): + """Tear down Selenium test resources. + + Cleans up the Selenium driver connection and soro server process + """ + for driver in self.drivers: + driver.quit() + + self.proc.terminate() + self.proc.wait() + self.proc.stdout.close() + self.proc.stderr.close() + + def assert_no_js_errors(self, driver): + """Assert the driver has not logged any JS errors.""" + # firefox only supports WebDriver BiDi and log.entryAdded events from + # the W3C specs (https://github.com/mozilla/geckodriver/issues/284), + # which Selenium does not (yet) support + # (https://github.com/SeleniumHQ/selenium/issues/10335) + # So we just check chrome for logged JS errors + if driver.name == "chrome": + for line in driver.get_log("browser"): + if line['source'] != "javascript": + continue + if line['level'] not in ["SEVERE"]: + continue + self.fail("driver reported a JavaScript error " + + f"({line['level']}): {line['message']}") + + +class SimulationButtonTest(SeleniumTestCase): + """Test case validating the simulation button.""" + + def test_simulation_button(self): + """Basic tast of the simulation button. + + Validates that clicking the button results in an active simulation tab. + """ + for driver in self.drivers: + with self.subTest(f"driver {driver.name}"): + driver.get(target) + + overlayToggle = driver.find_element( + By.ID, + "overlayToggleButton" + ) + overlayToggle.click() + + simButton = WebDriverWait(driver, 10).until( + EC.element_to_be_clickable( + (By.ID, "addInfrastructureComponentButton")) + ) + simButton.click() + normClass = 'concat(" ", normalize-space(@class), " ")' + divFilter = " and ".join([ + f'contains({normClass}, " lm_active ")', + f'contains({normClass}, " lm_tab ")', + '@title="Simulation"' + ]) + xpath = f'//section/div[{divFilter}]' + WebDriverWait(driver, 10).until( + EC.presence_of_element_located((By.XPATH, xpath)) + ) + + self.assert_no_js_errors(driver) + + +if __name__ == "__main__": + unittest.main() diff --git a/ui-test/default.nix b/ui-test/default.nix new file mode 100644 index 00000000..53f424b0 --- /dev/null +++ b/ui-test/default.nix @@ -0,0 +1,81 @@ +{ nix-filter, nixosTest, stdenv, soro-s, python3, chromedriver, chromium }: + +let + system = "x86_64-linux"; + + username = "soro-s"; + + testScripts = stdenv.mkDerivation { + name = "soro-s-ui-tests"; + src = nix-filter.lib.filter { + root = ./.; + exclude = [ (nix-filter.lib.matchExt "nix") ]; + }; + + buildPhase = '' + echo "#!/usr/bin/env bash" > run-ui-tests.sh + echo "set -e" >> run-ui-tests.sh + find . -type f -executable | sed 's#^./#'$out/bin/'#' >> run-ui-tests.sh + chmod +x run-ui-tests.sh + ''; + + installPhase = '' + mkdir -p $out/bin + find . -type f -executable -exec cp {} $out/bin \; + ''; + }; +in nixosTest { + name = "soros-ui-tests"; + + nodes = { + server = { config, pkgs, ... }: { + + systemd.services.soro-server = { + enable = true; + description = "Stochastic Ordering-Based Railway Operations Simulation and Optimization"; + serviceConfig = { + Type = "simple"; + ExecStartPre = pkgs.writeScript "setup-soro-server.sh" '' + #!/bin/sh + for d in {resources,server_resources,profile}; do + if [ ! -e "/var/lib/soro-server/$d" ]; then + cp -r "${soro-s}/bin/$d" "/var/lib/soro-server/$d" + chmod -R ug+w "/var/lib/soro-server/$d" + fi + done + ''; + ExecStart = "${soro-s}/bin/soro-server --port 8080 " + + "--resource_dir /var/lib/soro-server/resources " + + "--server_resource_dir /var/lib/soro-server/server_resources"; + + DynamicUser = true; + BindReadOnlyPaths = "/nix/store"; + RuntimeDirectory = "soro-server"; + StateDirectory = "soro-server"; + WorkingDirectory = "/var/lib/soro-server"; + }; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + }; + + environment.systemPackages = [ + (python3.withPackages (pp: [ pp.selenium ])) + chromedriver chromium testScripts + ]; + + users = { + mutableUsers = false; + }; + }; + }; + + testScript = '' + start_all() + + server.wait_for_unit("soro-server.service") + server.wait_for_open_port(8080) + + server.succeed("run-ui-tests.sh") + ''; +} diff --git a/ui-test/docker-compose.yml b/ui-test/docker-compose.yml new file mode 100644 index 00000000..a678ed72 --- /dev/null +++ b/ui-test/docker-compose.yml @@ -0,0 +1,34 @@ +# https://github.com/SeleniumHQ/docker-selenium/blob/trunk/docker-compose-v3.yml +version: "3" +services: + chrome: + image: selenium/node-chrome:4.7.2-20221219 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + extra_hosts: + - "host.docker.internal:host-gateway" + + firefox: + image: selenium/node-firefox:4.7.2-20221219 + shm_size: 2gb + depends_on: + - selenium-hub + environment: + - SE_EVENT_BUS_HOST=selenium-hub + - SE_EVENT_BUS_PUBLISH_PORT=4442 + - SE_EVENT_BUS_SUBSCRIBE_PORT=4443 + extra_hosts: + - "host.docker.internal:host-gateway" + + selenium-hub: + image: selenium/hub:4.7.2-20221219 + container_name: selenium-hub + ports: + - "4442:4442" + - "4443:4443" + - "4444:4444" diff --git a/ui-test/requirements.txt b/ui-test/requirements.txt new file mode 100644 index 00000000..5b0c9827 --- /dev/null +++ b/ui-test/requirements.txt @@ -0,0 +1 @@ +selenium==4.7.2 From ea6dcf7dc9b64ea4fe2cb8989ce8a84454718fb5 Mon Sep 17 00:00:00 2001 From: Julian Harbarth Date: Wed, 25 Jan 2023 13:21:40 +0100 Subject: [PATCH 04/51] Parse LZB/ETCS (#44) * parse detailed lzb/etcs information * add line model * track etcs elements in station routes * split into .h/.cc * add alias * add precondition/postcondition alias for asserts * don't generate IRs when etcs is required * fix sassert for release * change line precons into soft preconditions * fix linting issue * fix linting issues --- include/soro/infrastructure/graph/element.h | 21 ++--- .../infrastructure/graph/graph_creation.h | 2 +- include/soro/infrastructure/graph/section.h | 2 +- include/soro/infrastructure/graph/type.h | 15 +++- .../soro/infrastructure/infrastructure_t.h | 3 + include/soro/infrastructure/kilometrage.h | 9 ++ include/soro/infrastructure/line.h | 30 +++++++ .../parsers/iss/iss_string_literals.h | 15 ++-- .../parsers/iss/parse_helpers.h | 2 +- .../parsers/iss/parse_track_element.h | 2 +- include/soro/infrastructure/regulatory_data.h | 7 +- include/soro/infrastructure/station/station.h | 4 +- .../infrastructure/station/station_route.h | 5 ++ include/soro/utls/sassert.h | 43 +++++++++- include/soro/utls/std_wrapper/std_wrapper.h | 4 +- src/infrastructure/graph/graph_creation.cc | 14 +-- src/infrastructure/graph/type.cc | 30 ++++--- .../get_interlocking_subsystem.cc | 37 ++------ src/infrastructure/line.cc | 46 ++++++++++ src/infrastructure/parsers/iss/iss_files.cc | 1 + .../parsers/iss/parse_helpers.cc | 4 +- src/infrastructure/parsers/iss/parse_iss.cc | 76 +++++++++++----- .../parsers/iss/parse_track_element.cc | 7 +- src/infrastructure/regulatory_data.cc | 86 ++++++++++++++++++- src/infrastructure/station/station_route.cc | 11 +++ test/src/infrastructure/graph_test.cc | 7 +- test/src/infrastructure/section_test.cc | 19 ++-- 27 files changed, 381 insertions(+), 121 deletions(-) create mode 100644 include/soro/infrastructure/kilometrage.h create mode 100644 include/soro/infrastructure/line.h create mode 100644 src/infrastructure/line.cc diff --git a/include/soro/infrastructure/graph/element.h b/include/soro/infrastructure/graph/element.h index 7c3fa34b..548f9e6b 100644 --- a/include/soro/infrastructure/graph/element.h +++ b/include/soro/infrastructure/graph/element.h @@ -10,6 +10,8 @@ #include "soro/si/units.h" #include "soro/infrastructure/graph/type.h" +#include "soro/infrastructure/kilometrage.h" +#include "soro/infrastructure/line.h" namespace soro::infra { @@ -29,8 +31,7 @@ auto const INVALID_ELEMENT_ID = std::numeric_limits::max(); using kilometrage = si::length; -using line_id = uint32_t; -constexpr auto const INVALID_LINE_ID = std::numeric_limits::max(); +constexpr auto const INVALID_LINE_ID = std::numeric_limits::max(); struct end_element { #if !(defined(SERIALIZE) || defined(__EMSCRIPTEN__)) @@ -66,7 +67,7 @@ struct end_element { soro::array neighbours_{nullptr, nullptr}; kilometrage km_{si::INVALID}; - line_id line_{INVALID_LINE_ID}; + line::id line_{INVALID_LINE_ID}; }; struct simple_element { @@ -108,8 +109,8 @@ struct simple_element { kilometrage end_km_{si::INVALID}; kilometrage start_km_{si::INVALID}; - line_id end_line_{INVALID_LINE_ID}; - line_id start_line_{INVALID_LINE_ID}; + line::id end_line_{INVALID_LINE_ID}; + line::id start_line_{INVALID_LINE_ID}; bool end_rising_{false}; }; @@ -144,7 +145,7 @@ struct track_element { soro::array neighbours_{nullptr, nullptr}; kilometrage km_{si::INVALID}; - line_id line_{INVALID_LINE_ID}; + line::id line_{INVALID_LINE_ID}; }; struct undirected_track_element { @@ -185,7 +186,7 @@ struct undirected_track_element { soro::array neighbours_{nullptr, nullptr, nullptr, nullptr}; kilometrage km_{si::INVALID}; - line_id line_{INVALID_LINE_ID}; + line::id line_{INVALID_LINE_ID}; }; struct simple_switch { @@ -230,7 +231,7 @@ struct simple_switch { soro::array neighbours_{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; - line_id start_line_{INVALID_LINE_ID}, stem_line_{INVALID_LINE_ID}, + line::id start_line_{INVALID_LINE_ID}, stem_line_{INVALID_LINE_ID}, branch_line_{INVALID_LINE_ID}; kilometrage start_km_{si::INVALID}, stem_km_{si::INVALID}, branch_km_{si::INVALID}; @@ -291,8 +292,8 @@ struct cross { bool start_left_end_right_arc_{false}; bool start_right_end_left_arc_{false}; - line_id start_left_line_{INVALID_LINE_ID}, end_left_line_{INVALID_LINE_ID}; - line_id start_right_line_{INVALID_LINE_ID}, end_right_line_{INVALID_LINE_ID}; + line::id start_left_line_{INVALID_LINE_ID}, end_left_line_{INVALID_LINE_ID}; + line::id start_right_line_{INVALID_LINE_ID}, end_right_line_{INVALID_LINE_ID}; kilometrage start_left_km_{si::INVALID}, end_left_km_{si::INVALID}; kilometrage start_right_km_{si::INVALID}, diff --git a/include/soro/infrastructure/graph/graph_creation.h b/include/soro/infrastructure/graph/graph_creation.h index 2c51b02e..7d27fe42 100644 --- a/include/soro/infrastructure/graph/graph_creation.h +++ b/include/soro/infrastructure/graph/graph_creation.h @@ -57,7 +57,7 @@ element* get_or_create_element(graph& network, station& station, bool const rising); void set_km_point_and_line(element& e, std::string const& node_name, - kilometrage km_point, line_id line); + kilometrage km_point, line::id line); void set_neighbour(element& e, std::string const& name, element* neigh, bool const rising); diff --git a/include/soro/infrastructure/graph/section.h b/include/soro/infrastructure/graph/section.h index 5fef6c12..e49285c5 100644 --- a/include/soro/infrastructure/graph/section.h +++ b/include/soro/infrastructure/graph/section.h @@ -48,7 +48,7 @@ struct section { soro::vector falling_order_; si::length length_{si::ZERO}; - line_id line_id_{INVALID_LINE_ID}; + line::id line_id_{INVALID_LINE_ID}; }; } // namespace soro::infra diff --git a/include/soro/infrastructure/graph/type.h b/include/soro/infrastructure/graph/type.h index e12be5b4..6f06be48 100644 --- a/include/soro/infrastructure/graph/type.h +++ b/include/soro/infrastructure/graph/type.h @@ -29,7 +29,12 @@ enum class type : type_id { SPEED_LIMIT, POINT_SPEED, BRAKE_PATH, - CTC, + LZB_START, + LZB_END, + LZB_BLOCK_SIGN, + ETCS_START, + ETCS_END, + ETCS_BLOCK_SIGN, FORCED_HALT, HALT, // undirected track elements @@ -103,4 +108,12 @@ constexpr bool is_runtime_checkpoint(type const type) { constexpr bool is_halt(type const type) { return type == type::HALT; } +constexpr bool is_lzb(type const type) { + return type >= type::LZB_START && type <= type::LZB_BLOCK_SIGN; +} + +constexpr bool is_etcs(type const type) { + return type >= type::ETCS_START && type <= type::ETCS_BLOCK_SIGN; +} + } // namespace soro::infra diff --git a/include/soro/infrastructure/infrastructure_t.h b/include/soro/infrastructure/infrastructure_t.h index 30633a72..66846613 100644 --- a/include/soro/infrastructure/infrastructure_t.h +++ b/include/soro/infrastructure/infrastructure_t.h @@ -5,6 +5,7 @@ #include "soro/infrastructure/graph/graph.h" #include "soro/infrastructure/infrastructure_options.h" #include "soro/infrastructure/interlocking/interlocking_subsystem.h" +#include "soro/infrastructure/line.h" #include "soro/infrastructure/station/station.h" #include "soro/infrastructure/station/station_route_graph.h" #include "soro/rolling_stock/rolling_stock.h" @@ -42,6 +43,8 @@ struct infrastructure_t { soro::vector station_positions_{}; soro::vector element_positions_{}; + lines lines_{}; + soro::vector> station_store_{}; soro::vector> station_route_store_{}; soro::vector> diff --git a/include/soro/infrastructure/kilometrage.h b/include/soro/infrastructure/kilometrage.h new file mode 100644 index 00000000..53c1c28d --- /dev/null +++ b/include/soro/infrastructure/kilometrage.h @@ -0,0 +1,9 @@ +#pragma once + +#include "soro/si/units.h" + +namespace soro::infra { + +using kilometrage = si::length; + +} // namespace soro::infra \ No newline at end of file diff --git a/include/soro/infrastructure/line.h b/include/soro/infrastructure/line.h new file mode 100644 index 00000000..44f519b1 --- /dev/null +++ b/include/soro/infrastructure/line.h @@ -0,0 +1,30 @@ +#pragma once + +#include "soro/base/soro_types.h" +#include "soro/infrastructure/kilometrage.h" + +namespace soro::infra { + +struct line { + using id = uint16_t; + + struct segment { + kilometrage from_{si::INVALID}; + kilometrage to_{si::INVALID}; + bool etcs_{false}; + bool lzb_{false}; + bool signalling_{false}; + }; + + bool has_signalling(kilometrage const km) const; + bool has_etcs(kilometrage const km) const; + + id id_; + + // ordered w.r.t. distance + soro::vector segments_; +}; + +using lines = soro::map; + +} // namespace soro::infra diff --git a/include/soro/infrastructure/parsers/iss/iss_string_literals.h b/include/soro/infrastructure/parsers/iss/iss_string_literals.h index 7ce1431a..b210c718 100644 --- a/include/soro/infrastructure/parsers/iss/iss_string_literals.h +++ b/include/soro/infrastructure/parsers/iss/iss_string_literals.h @@ -34,14 +34,17 @@ constexpr const char* const ETCS_START_FALLING = "ETCSAnfangF"; constexpr const char* const ETCS_END_RISING = "ETCSEndeS"; constexpr const char* const ETCS_END_FALLING = "ETCSEndeF"; -constexpr const char* const CTC_START_FALLING = "LZBAnfangF"; -constexpr const char* const CTC_END_FALLING = "LZBEndeF"; +constexpr const char* const ETCS_BLOCK_SIGN_RISING = "ETCSBlockkennzeichenS"; +constexpr const char* const ETCS_BLOCK_SIGN_FALLING = "ETCSBlockkennzeichenF"; -constexpr const char* const CTC_START_RISING = "LZBAnfangS"; -constexpr const char* const CTC_END_RISING = "LZBEndeS"; +constexpr const char* const LZB_START_FALLING = "LZBAnfangF"; +constexpr const char* const LZB_END_FALLING = "LZBEndeF"; -constexpr const char* const CTC_BLOCK_SIGN_RISING = "LZBBlockkennzeichenS"; -constexpr const char* const CTC_BLOCK_SIGN_FALLING = "LZBBlockkennzeichenF"; +constexpr const char* const LZB_START_RISING = "LZBAnfangS"; +constexpr const char* const LZB_END_RISING = "LZBEndeS"; + +constexpr const char* const LZB_BLOCK_SIGN_RISING = "LZBBlockkennzeichenS"; +constexpr const char* const LZB_BLOCK_SIGN_FALLING = "LZBBlockkennzeichenF"; constexpr const char* const SPEED_LIMIT_RISING = "GeschwZulaessigS"; constexpr const char* const SPEED_LIMIT_FALLING = "GeschwZulaessigF"; diff --git a/include/soro/infrastructure/parsers/iss/parse_helpers.h b/include/soro/infrastructure/parsers/iss/parse_helpers.h index 24350e62..90dea08f 100644 --- a/include/soro/infrastructure/parsers/iss/parse_helpers.h +++ b/include/soro/infrastructure/parsers/iss/parse_helpers.h @@ -7,7 +7,7 @@ namespace soro::infra { -kilometrage parse_kilometrage(pugi::xml_node const& node); +kilometrage parse_kilometrage(std::string_view const kmp); rail_plan_node_id parse_rp_node_id(pugi::xml_node const& node); diff --git a/include/soro/infrastructure/parsers/iss/parse_track_element.h b/include/soro/infrastructure/parsers/iss/parse_track_element.h index f2e022ab..2ec98b3d 100644 --- a/include/soro/infrastructure/parsers/iss/parse_track_element.h +++ b/include/soro/infrastructure/parsers/iss/parse_track_element.h @@ -9,7 +9,7 @@ namespace soro::infra { element* parse_track_element(pugi::xml_node const& track_node, type const type, - bool const rising, line_id const line, + bool const rising, line::id const line, graph& network, station& station, construction_materials& mats); diff --git a/include/soro/infrastructure/regulatory_data.h b/include/soro/infrastructure/regulatory_data.h index 6354e9ef..06ea7a6f 100644 --- a/include/soro/infrastructure/regulatory_data.h +++ b/include/soro/infrastructure/regulatory_data.h @@ -1,6 +1,7 @@ #pragma once #include "soro/base/soro_types.h" +#include "soro/infrastructure/line.h" #include "soro/utls/file/loaded_file.h" namespace soro::infra { @@ -9,12 +10,10 @@ struct regulatory_station_data { soro::map ds100_to_full_name_{}; }; -struct regulatory_line_data {}; - regulatory_station_data parse_regulatory_stations( std::vector const& regulatory_station_files); -regulatory_line_data parse_regulatory_line_data( - std::vector const&); +soro::map parse_lines( + std::vector const& regulatory_line_files); } // namespace soro::infra \ No newline at end of file diff --git a/include/soro/infrastructure/station/station.h b/include/soro/infrastructure/station/station.h index 6f1f0f84..74d64b4f 100644 --- a/include/soro/infrastructure/station/station.h +++ b/include/soro/infrastructure/station/station.h @@ -44,7 +44,7 @@ struct border { std::numeric_limits::max(); // minimum information to uniquely identify a border pair - using id_tuple = std::tuple; + using id_tuple = std::tuple; auto get_id_tuple() const { return id_tuple{std::min(station_->id_, neighbour_->id_), @@ -62,7 +62,7 @@ struct border { station::ptr neighbour_{nullptr}; element::ptr neighbour_element_{nullptr}; - line_id line_{INVALID_LINE_ID}; + line::id line_{INVALID_LINE_ID}; track_sign track_sign_{INVALID_TRACK_SIGN}; bool low_border_{false}; diff --git a/include/soro/infrastructure/station/station_route.h b/include/soro/infrastructure/station/station_route.h index c63aee49..084fe23e 100644 --- a/include/soro/infrastructure/station/station_route.h +++ b/include/soro/infrastructure/station/station_route.h @@ -50,6 +50,9 @@ struct station_route { soro::vector nodes_{}; soro::vector main_signals_{}; + + soro::vector etcs_starts_{}; + soro::vector etcs_ends_{}; }; node::idx size() const noexcept; @@ -69,6 +72,8 @@ struct station_route { bool can_start_an_interlocking(station_route_graph const& srg) const; bool can_end_an_interlocking(station_route_graph const&) const; + bool requires_etcs(lines const& lines) const; + bool operator==(station_route const& o) const; bool operator!=(station_route const& o) const; diff --git a/include/soro/utls/sassert.h b/include/soro/utls/sassert.h index d74fdd2d..fc80a966 100644 --- a/include/soro/utls/sassert.h +++ b/include/soro/utls/sassert.h @@ -46,8 +46,11 @@ struct bool_with_loc { }; #if !defined(NDEBUG) + template -inline void sassert(bool_with_loc assert_this, Msg&& msg, Args... args) { +inline void sassert_impl(bool_with_loc assert_this, + std::string_view failure_type, Msg&& msg, + Args... args) { if (!assert_this) { using clock = std::chrono::system_clock; @@ -62,7 +65,7 @@ inline void sassert(bool_with_loc assert_this, Msg&& msg, Args... args) { std::stringstream ss; - fmt::print(ss, "[ASSERT FAIL][{}] ", std::put_time(&tmp, "%FT%TZ")); + fmt::print(ss, "[{}][{}] ", failure_type, std::put_time(&tmp, "%FT%TZ")); fmt::print(ss, std::forward(msg), std::forward(args)...); fmt::print(ss, "\n"); fmt::print(ss, "[FAILED HERE] {}:{}:{} in {}", @@ -79,12 +82,27 @@ inline void sassert(bool_with_loc assert_this, Msg&& msg, Args... args) { #else template -inline void sassert(bool const, Msg&&, Args...) {} +inline void sassert_impl(bool const, Msg&&, Args...) {} #endif +template +inline void sassert(bool_with_loc assert_this, Msg&& msg, Args... args) { + sassert_impl(assert_this, "ASSERT", msg, args...); +} + +template +inline void expects(bool_with_loc assert_this, Msg&& msg, Args... args) { + sassert_impl(assert_this, "PRECONDITION", msg, args...); +} + +template +inline void ensures(bool_with_loc assert_this, Msg&& msg, Args... args) { + sassert_impl(assert_this, "POSTCONDITION", msg, args...); +} + inline void sassert(bool const assert_this) { - sassert(assert_this, "I didn't specify a error message :("); + sassert_impl(assert_this, "Unknown", "I didn't specify a error message :("); } #if !defined(NDEBUG) @@ -92,9 +110,26 @@ template inline void sasserts(F&& f) { f(); } + +template +inline void expects(F&& f) { + f(); +} + +template +inline void ensures(F&& f) { + f(); +} + #else template inline void sasserts(F&&) {} + +template +inline void expects(F&&) {} + +template +inline void ensures(F&&) {} #endif } // namespace soro::utls \ No newline at end of file diff --git a/include/soro/utls/std_wrapper/std_wrapper.h b/include/soro/utls/std_wrapper/std_wrapper.h index bb91db07..7ed11eee 100644 --- a/include/soro/utls/std_wrapper/std_wrapper.h +++ b/include/soro/utls/std_wrapper/std_wrapper.h @@ -156,8 +156,8 @@ inline void append_move(C1& dest, C2&& src) { } template -inline auto any_of(Iteratable const& i, Pred&& p) { - return std::any_of(std::cbegin(i), std::cend(i), p); +inline auto any_of(Iteratable&& i, Pred&& p) { + return std::any_of(std::begin(i), std::end(i), p); } template diff --git a/src/infrastructure/graph/graph_creation.cc b/src/infrastructure/graph/graph_creation.cc index 097e3526..d2811fd4 100644 --- a/src/infrastructure/graph/graph_creation.cc +++ b/src/infrastructure/graph/graph_creation.cc @@ -48,13 +48,13 @@ element* get_or_create_element(graph& network, station& station, } void set_km_point_and_line(end_element& e, std::string const&, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { e.km_ = km_point; e.line_ = line; } void set_km_point_and_line(simple_element& e, std::string const& node_name, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { switch (str_hash(node_name)) { case str_hash(KM_JUMP_START): case str_hash(LINE_SWITCH_ZERO): { @@ -83,19 +83,19 @@ void set_km_point_and_line(simple_element& e, std::string const& node_name, } void set_km_point_and_line(track_element& e, std::string const&, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { e.km_ = km_point; e.line_ = line; } void set_km_point_and_line(undirected_track_element& e, std::string const&, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { e.km_ = km_point; e.line_ = line; } void set_km_point_and_line(simple_switch& e, std::string const& node_name, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { switch (str_hash(node_name)) { case str_hash(SWITCH_START): { e.start_km_ = km_point; @@ -119,7 +119,7 @@ void set_km_point_and_line(simple_switch& e, std::string const& node_name, } void set_km_point_and_line(cross& e, std::string const& node_name, - kilometrage const km_point, line_id const line) { + kilometrage const km_point, line::id const line) { switch (str_hash(node_name)) { case str_hash(CROSS_SWITCH_START_LEFT): case str_hash(CROSS_START_LEFT): { @@ -150,7 +150,7 @@ void set_km_point_and_line(cross& e, std::string const& node_name, } void set_km_point_and_line(element& e, std::string const& node_name, - kilometrage km_point, line_id line) { + kilometrage km_point, line::id line) { e.apply( [&](auto&& x) { set_km_point_and_line(x, node_name, km_point, line); }); } diff --git a/src/infrastructure/graph/type.cc b/src/infrastructure/graph/type.cc index bb3a544f..13b210c5 100644 --- a/src/infrastructure/graph/type.cc +++ b/src/infrastructure/graph/type.cc @@ -80,18 +80,19 @@ type get_type(const char* const str) { case str_hash(CROSS_SWITCH_END_LEFT): case str_hash(CROSS_SWITCH_START_RIGHT): case str_hash(CROSS_SWITCH_END_RIGHT): return type::CROSS; - case str_hash(CTC_BLOCK_SIGN_RISING): - // ct - case str_hash(CTC_BLOCK_SIGN_FALLING): - case str_hash(CTC_START_FALLING): - case str_hash(CTC_END_FALLING): - case str_hash(CTC_START_RISING): - case str_hash(CTC_END_RISING): - case str_hash(ETCS_END_RISING): - case str_hash(ETCS_END_FALLING): + case str_hash(LZB_START_RISING): + case str_hash(LZB_START_FALLING): return type::LZB_START; + case str_hash(LZB_END_RISING): + case str_hash(LZB_END_FALLING): return type::LZB_END; + case str_hash(LZB_BLOCK_SIGN_RISING): + case str_hash(LZB_BLOCK_SIGN_FALLING): return type::LZB_BLOCK_SIGN; case str_hash(ETCS_START_RISING): - case str_hash(ETCS_START_FALLING): - return type::CTC; + case str_hash(ETCS_START_FALLING): return type::ETCS_START; + case str_hash(ETCS_END_RISING): + case str_hash(ETCS_END_FALLING): return type::ETCS_END; + case str_hash(ETCS_BLOCK_SIGN_RISING): + case str_hash(ETCS_BLOCK_SIGN_FALLING): + return type::ETCS_BLOCK_SIGN; // ignore these for the moment default: case str_hash(PICTURE_POINT): @@ -127,7 +128,12 @@ std::string get_type_str(type const& t) { case type::SLOPE: return "slope"; case type::LEVEL_CROSSING: return "level_crossing"; case type::CROSS: return "cross"; - case type::CTC: return "ctc"; + case type::LZB_END: return "lzb_end"; + case type::LZB_START: return "lzb_start"; + case type::LZB_BLOCK_SIGN: return "lzb_block_sign"; + case type::ETCS_END: return "etcs_end"; + case type::ETCS_START: return "etcs_start"; + case type::ETCS_BLOCK_SIGN: return "etcs_block_sign"; } throw utl::fail("No type string found in infrastructure element"); } diff --git a/src/infrastructure/interlocking/get_interlocking_subsystem.cc b/src/infrastructure/interlocking/get_interlocking_subsystem.cc index a84eea78..12fb37f9 100644 --- a/src/infrastructure/interlocking/get_interlocking_subsystem.cc +++ b/src/infrastructure/interlocking/get_interlocking_subsystem.cc @@ -147,37 +147,11 @@ interlocking_route get_trailing_interlocking_route( .station_routes_ = {sr->id_}}; } -std::string generate_dot_tree(station_route::ptr const route, - station_route_graph const& srg) { - std::set ids; - - auto const generate_graphviz = [&](station_route::ptr const sr, - auto&& generate_ref) { - ids.insert(sr->id_); - - if (sr->can_end_an_interlocking(srg) && sr->id_ != route->id_) { - return; - } - - for (auto const& neighbour : srg.successors_[sr->id_]) { - generate_ref(neighbour, generate_ref); - } - }; - - generate_graphviz(route, generate_graphviz); - - std::string dot; - for (auto const& id : ids) { - for (auto const& neigh : srg.successors_[id]) { - dot += std::to_string(id) + " -> " + std::to_string(neigh->id_) + ";\n"; - } - } - - return dot; -} - soro::vector get_interlocking_routes_from_sr( station_route::ptr sr, station_route_graph const& srg) { + utls::expects(sr->can_start_an_interlocking(srg), + "SR {} cannot start an interlocking route.", sr->id_); + soro::vector routes; // reserve 2^16 elements as around there is the maximum of interlocking @@ -225,7 +199,10 @@ soro::vector get_interlocking_routes( interlocking_routes.reserve(infra.station_routes_.size() * 30); for (auto const& sr : infra.station_routes_) { - if (sr->can_start_an_interlocking(infra.station_route_graph_)) { + // add all interlocking routes that start with this station route. + // do not generate them when etcs is required to use the station route. + if (sr->can_start_an_interlocking(infra.station_route_graph_) && + !sr->requires_etcs(infra.lines_)) { utl::concat(interlocking_routes, get_interlocking_routes_from_sr( sr, infra.station_route_graph_)); } diff --git a/src/infrastructure/line.cc b/src/infrastructure/line.cc new file mode 100644 index 00000000..4c192365 --- /dev/null +++ b/src/infrastructure/line.cc @@ -0,0 +1,46 @@ +#include "soro/infrastructure/line.h" + +#include "utl/logging.h" + +namespace soro::infra { + +line::segment const& get_segment(line const& l, kilometrage const km) { + utls::expects(!l.segments_.empty(), "Segments of line {} are empty.", l.id_); + + // ideally these would be a precondition, alas + if (km < l.segments_.front().from_) { + uLOG(utl::warn) << "Requesting line segment with kilometrage " << km + << ", but first line segment only starts at " + << l.segments_.front().from_ << ". Serving first segment."; + return l.segments_.front(); + } + + if (km > l.segments_.back().to_) { + uLOG(utl::warn) << "Requesting line segment with kilometrage " << km + << ", but last line segment only goes to " + << l.segments_.back().to_ << ". Serving last segment."; + return l.segments_.back(); + } + + auto const it = + std::lower_bound( + std::begin(l.segments_), std::end(l.segments_), km, + [](auto&& segment, auto&& v) { return segment.from_ <= v; }) - + 1; + + utls::sassert(it->from_ <= km && km <= it->to_, + "Segment of line {} does not contain kilometrage {}.", l.id_, + km); + + return *it; +} + +bool line::has_signalling(kilometrage const km) const { + return get_segment(*this, km).signalling_; +} + +bool line::has_etcs(kilometrage const km) const { + return get_segment(*this, km).etcs_; +} + +} // namespace soro::infra \ No newline at end of file diff --git a/src/infrastructure/parsers/iss/iss_files.cc b/src/infrastructure/parsers/iss/iss_files.cc index 8c687a25..e77414c7 100644 --- a/src/infrastructure/parsers/iss/iss_files.cc +++ b/src/infrastructure/parsers/iss/iss_files.cc @@ -21,6 +21,7 @@ void add_iss_file(iss_files& iss_fs, std::filesystem::path const& fp) { } else if (fp.filename().string().starts_with("Ordnungsrahmen_ORStr")) { iss_fs.regulatory_line_files_.emplace_back(fp); } else if (fp.filename().string().starts_with("BaubetrieblicheMassnahmen")) { + // ignore for now } else { uLOG(utl::warn) << "Found file " << fp diff --git a/src/infrastructure/parsers/iss/parse_helpers.cc b/src/infrastructure/parsers/iss/parse_helpers.cc index a02498fb..1bf51ef2 100644 --- a/src/infrastructure/parsers/iss/parse_helpers.cc +++ b/src/infrastructure/parsers/iss/parse_helpers.cc @@ -8,9 +8,7 @@ namespace soro::infra { using namespace soro::utls; -kilometrage parse_kilometrage(pugi::xml_node const& node) { - std::string_view const kmp(node.child_value(KILOMETER_POINT)); - +kilometrage parse_kilometrage(std::string_view kmp) { if (auto const pos = kmp.find('+'); pos != std::string::npos) { auto const base_km = parse_fp( diff --git a/src/infrastructure/parsers/iss/parse_iss.cc b/src/infrastructure/parsers/iss/parse_iss.cc index 1d2663d2..63c1f851 100644 --- a/src/infrastructure/parsers/iss/parse_iss.cc +++ b/src/infrastructure/parsers/iss/parse_iss.cc @@ -16,6 +16,7 @@ #include "soro/utls/execute_if.h" #include "soro/utls/parse_fp.h" +#include "soro/utls/parse_int.h" #include "soro/utls/sassert.h" #include "soro/utls/string.h" @@ -45,14 +46,16 @@ using namespace soro::utls; length get_section_length(xml_node const& section_start, xml_node const& section_end) { - auto const start_km = parse_kilometrage(section_start); - auto const end_km = parse_kilometrage(section_end); + auto const start_km = + parse_kilometrage(section_start.child_value(KILOMETER_POINT)); + auto const end_km = + parse_kilometrage(section_end.child_value(KILOMETER_POINT)); return abs(start_km - end_km); } border parse_border(xml_node const& xml_rp_border, element* border_element, - line_id const line, bool const is_start) { + line::id const line, bool const is_start) { border b; b.neighbour_name_ = xml_rp_border.child_value(PARTNER_STATION); @@ -104,12 +107,17 @@ constexpr type_order_key get_type_order_key(type const t, bool const rising) { case type::APPROACH_SIGNAL: return rising ? 130 : 131; case type::PROTECTION_SIGNAL: return rising ? 140 : 141; case type::EOTD: return rising ? 150 : 151; - case type::CTC: return rising ? 160 : 161; - case type::SPEED_LIMIT: return rising ? 170 : 171; - case type::FORCED_HALT: return rising ? 180 : 181; - case type::POINT_SPEED: return rising ? 190 : 191; + case type::LZB_START: return rising ? 160 : 161; + case type::LZB_BLOCK_SIGN: return rising ? 170 : 171; + case type::LZB_END: return rising ? 180 : 181; + case type::ETCS_START: return rising ? 190 : 191; + case type::ETCS_BLOCK_SIGN: return rising ? 200 : 201; + case type::ETCS_END: return rising ? 210 : 211; + case type::SPEED_LIMIT: return rising ? 220 : 221; + case type::FORCED_HALT: return rising ? 230 : 231; + case type::POINT_SPEED: return rising ? 240 : 241; case type::BRAKE_PATH: - return rising ? 200 : 201; + return rising ? 250 : 251; // undirected track elements part 2 case type::TRACK_NAME: return 300; @@ -158,8 +166,8 @@ bool order_impl(kilometrage const km1, type const t1, bool const r1, template bool element_order_from_nodes(xml_node const n1, xml_node const n2) { - auto const km1 = parse_kilometrage(n1); - auto const km2 = parse_kilometrage(n2); + auto const km1 = parse_kilometrage(n1.child_value(KILOMETER_POINT)); + auto const km2 = parse_kilometrage(n2.child_value(KILOMETER_POINT)); auto const r1 = has_rising_name(n1); auto const r2 = has_rising_name(n2); @@ -197,8 +205,8 @@ bool element_order(element::ptr e1, element::ptr e2) { section::id parse_section_into_network(xml_node const& xml_rp_section, station& station, graph& network, construction_materials& mats) { - line_id const line = - static_cast(std::stoul(xml_rp_section.child_value(LINE))); + auto const line_id = + utls::parse_int(xml_rp_section.child_value(LINE)); auto const& section_start = xml_rp_section.child(RAIL_PLAN_NODE).children().begin(); auto const& section_end = @@ -207,7 +215,7 @@ section::id parse_section_into_network(xml_node const& xml_rp_section, auto const section_id = create_section(network); auto& sec = network.sections_[section_id]; sec.length_ = get_section_length(*section_start, *section_end); - sec.line_id_ = line; + sec.line_id_ = line_id; auto get_main_rp_node_id = [](auto const& node) -> rail_plan_node_id { switch (str_hash(node.name())) { @@ -309,20 +317,23 @@ section::id parse_section_into_network(xml_node const& xml_rp_section, } if (utls::equal(node.name(), BORDER)) { - station.borders_.push_back(parse_border(node, element, line, is_start)); + station.borders_.push_back( + parse_border(node, element, line_id, is_start)); } return element; }; auto start_element = emplace_into_network(*section_start, true); - set_km_point_and_line(*start_element, section_start->name(), - parse_kilometrage(*section_start), line); + set_km_point_and_line( + *start_element, section_start->name(), + parse_kilometrage(section_start->child_value(KILOMETER_POINT)), line_id); network.element_id_to_section_ids_[start_element->id()].push_back(section_id); auto end_element = emplace_into_network(*section_end, false); - set_km_point_and_line(*end_element, section_end->name(), - parse_kilometrage(*section_end), line); + set_km_point_and_line( + *end_element, section_end->name(), + parse_kilometrage(section_end->child_value(KILOMETER_POINT)), line_id); network.element_id_to_section_ids_[end_element->id()].push_back(section_id); std::map undirected_track_elements; @@ -335,7 +346,7 @@ section::id parse_section_into_network(xml_node const& xml_rp_section, auto const rising_element = has_rising_name(node); auto const track_element = parse_track_element( - node, type, rising_element, line, network, station, mats); + node, type, rising_element, line_id, network, station, mats); if (dir || prev_element->is_track_element()) { set_neighbour(*prev_element, prev_node.name(), track_element, true); @@ -361,7 +372,7 @@ section::id parse_section_into_network(xml_node const& xml_rp_section, auto const track_element = utl::get_or_create(undirected_track_elements, node, [&]() { - return parse_track_element(node, type, false, line, network, + return parse_track_element(node, type, false, line_id, network, station, mats); }); @@ -582,6 +593,24 @@ deduplicated_paths get_station_route_paths(infrastructure_t const& infra, return main_signals; }; + auto const get_etcs = [&](soro::vector const& nodes) + -> std::pair, soro::vector> { + soro::vector etcs_starts, etcs_ends; + + for (node::idx idx = 0; idx < static_cast(nodes.size()); ++idx) { + auto const& node = nodes[idx]; + if (node->is(type::ETCS_START)) { + etcs_starts.emplace_back(idx); + } + + if (node->is(type::ETCS_END)) { + etcs_ends.emplace_back(idx); + } + } + + return {etcs_starts, etcs_ends}; + }; + auto const& graph = infra.graph_; deduplicated_paths result; @@ -613,6 +642,7 @@ deduplicated_paths get_station_route_paths(infrastructure_t const& infra, auto nodes = get_path(i_sr, get_node(start, true), get_node(end, false)->id_); auto main_signals = get_main_signals(i_sr, nodes); + auto [etcs_starts, etcs_ends] = get_etcs(nodes); result.path_store_.emplace_back(); result.path_store_.back() = soro::make_unique( @@ -620,7 +650,9 @@ deduplicated_paths get_station_route_paths(infrastructure_t const& infra, .end_ = end, .course_ = i_sr.course_, .nodes_ = std::move(nodes), - .main_signals_ = std::move(main_signals)}); + .main_signals_ = std::move(main_signals), + .etcs_starts_ = std::move(etcs_starts), + .etcs_ends_ = std::move(etcs_ends)}); auto const path_ptr = result.path_store_.back().get(); result.paths_.emplace_back(path_ptr); @@ -1151,6 +1183,8 @@ infrastructure_t parse_iss(infrastructure_options const& options) { iss.full_station_names_ = get_full_station_names(iss, regulatory_station_data); + iss.lines_ = parse_lines(iss_files.regulatory_line_files_); + iss.station_route_graph_ = get_station_route_graph(iss.station_routes_, iss.graph_); diff --git a/src/infrastructure/parsers/iss/parse_track_element.cc b/src/infrastructure/parsers/iss/parse_track_element.cc index c58e8be8..d2166db7 100644 --- a/src/infrastructure/parsers/iss/parse_track_element.cc +++ b/src/infrastructure/parsers/iss/parse_track_element.cc @@ -173,14 +173,15 @@ void add_element_data(pugi::xml_node const& xml_node, element* element, } element* parse_track_element(xml_node const& track_node, type const type, - bool const rising, line_id const line, + bool const rising, line::id const line, graph& network, station& station, construction_materials& mats) { auto element = create_element(network, station, mats, type, parse_rp_node_id(track_node), rising); - set_km_point_and_line(*element, track_node.name(), - parse_kilometrage(track_node), line); + set_km_point_and_line( + *element, track_node.name(), + parse_kilometrage(track_node.child_value(KILOMETER_POINT)), line); add_element_data(track_node, element, network); diff --git a/src/infrastructure/regulatory_data.cc b/src/infrastructure/regulatory_data.cc index cb626de4..8a3355c8 100644 --- a/src/infrastructure/regulatory_data.cc +++ b/src/infrastructure/regulatory_data.cc @@ -2,10 +2,15 @@ #include "pugixml.hpp" +#include "utl/concat.h" #include "utl/logging.h" #include "utl/timer.h" +#include "soro/utls/parse_fp.h" +#include "soro/utls/parse_int.h" + #include "soro/infrastructure/parsers/iss/iss_string_literals.h" +#include "soro/infrastructure/parsers/iss/parse_helpers.h" namespace soro::infra { @@ -35,10 +40,83 @@ regulatory_station_data parse_regulatory_stations( return station_data; } -regulatory_line_data parse_regulatory_line_data( - std::vector const&) { - regulatory_line_data line_data; - return line_data; +using line_type_key = uint32_t; + +static const std::map HAS_SIGNALLING = { + {1, true}, {2, true}, {3, false}, {6, true}, {8, true}, + {9, true}, {12, true}, {13, false}, {14, false}, {15, true}, + {18, true}, {20, true}, {21, true}, {23, true}, {24, false}, +}; + +static const std::map HAS_LZB = { + {1, false}, {2, true}, {3, true}, {6, false}, {8, false}, + {9, false}, {12, false}, {13, false}, {14, false}, {15, false}, + {18, false}, {20, false}, {21, false}, {23, false}, {24, false}, +}; + +static const std::map HAS_ETCS = { + {1, false}, {2, false}, {3, false}, {6, false}, {8, true}, + {9, true}, {12, true}, {13, true}, {14, false}, {15, false}, + {18, true}, {20, false}, {21, false}, {23, true}, {24, false}, +}; + +soro::vector parse_lines_from_file( + utls::loaded_file const& regulatory_line_file) { + soro::vector lines; + + pugi::xml_document d; + auto success = + d.load_buffer(reinterpret_cast(regulatory_line_file.data()), + regulatory_line_file.size()); + utl::verify(success, "Bad xml: {}", success.description()); + + for (auto const& line_xml : + d.child(XML_ISS_DATA).child("Ordnungsrahmen").child("Strecken")) { + + line l; + + l.id_ = utls::parse_int(line_xml.child_value("Nr")); + + for (auto const line_segment_xml : line_xml.child("Streckenabschnitte")) { + line::segment s; + + s.from_ = + parse_kilometrage(line_segment_xml.child_value("KilometrierungVon")); + s.to_ = + parse_kilometrage(line_segment_xml.child_value("KilometrierungBis")); + + if (auto type = line_segment_xml.child("Art"); static_cast(type)) { + auto const line_key = utls::parse_int( + type.attribute("Schluessel").value()); + + s.signalling_ = HAS_SIGNALLING.at(line_key); + s.lzb_ = HAS_LZB.at(line_key); + s.etcs_ = HAS_ETCS.at(line_key); + } + + l.segments_.push_back(s); + } + + lines.push_back(l); + } + + return lines; +} + +soro::map parse_lines( + std::vector const& regulatory_line_files) { + soro::vector lines; + + for (auto const& f : regulatory_line_files) { + utl::concat(lines, parse_lines_from_file(f)); + } + + soro::map result; + for (auto const& l : lines) { + result.emplace(l.id_, l); + } + + return result; } } // namespace soro::infra \ No newline at end of file diff --git a/src/infrastructure/station/station_route.cc b/src/infrastructure/station/station_route.cc index 78f60132..2b671100 100644 --- a/src/infrastructure/station/station_route.cc +++ b/src/infrastructure/station/station_route.cc @@ -54,6 +54,17 @@ bool station_route::is_contained_route() const { return !starts_at_boundary(*this) && !ends_at_boundary(*this); } +bool station_route::requires_etcs(lines const& lines) const { + if (!path_->etcs_starts_.empty()) { + auto const& etcs_start = + nodes(path_->etcs_starts_.back())->element_->as(); + auto const& line = lines.at(etcs_start.line_); + return !line.has_signalling(etcs_start.km_); + } + + return false; +} + bool station_route::can_start_an_interlocking( station_route_graph const& srg) const { return !this->path_->main_signals_.empty() || srg.predeccesors_[id_].empty(); diff --git a/test/src/infrastructure/graph_test.cc b/test/src/infrastructure/graph_test.cc index 5a92c61f..5ad16149 100644 --- a/test/src/infrastructure/graph_test.cc +++ b/test/src/infrastructure/graph_test.cc @@ -29,7 +29,12 @@ constexpr std::array, {type::SPEED_LIMIT, 1}, {type::POINT_SPEED, 1}, {type::BRAKE_PATH, 1}, - {type::CTC, 1}, + {type::LZB_START, 1}, + {type::LZB_END, 1}, + {type::LZB_BLOCK_SIGN, 1}, + {type::ETCS_START, 1}, + {type::ETCS_END, 1}, + {type::ETCS_BLOCK_SIGN, 1}, {type::FORCED_HALT, 1}, {type::HALT, 1}, // undirected track elements diff --git a/test/src/infrastructure/section_test.cc b/test/src/infrastructure/section_test.cc index 9c80e266..c757f799 100644 --- a/test/src/infrastructure/section_test.cc +++ b/test/src/infrastructure/section_test.cc @@ -169,13 +169,18 @@ void check_track_elements_are_ordered_correctly(section const& section) { {type::APPROACH_SIGNAL, 7}, {type::PROTECTION_SIGNAL, 8}, {type::EOTD, 9}, - {type::CTC, 10}, - {type::SPEED_LIMIT, 11}, - {type::FORCED_HALT, 12}, - {type::POINT_SPEED, 13}, - {type::BRAKE_PATH, 14}, - {type::TRACK_NAME, 15}, - {type::LEVEL_CROSSING, 16}, + {type::LZB_START, 10}, + {type::LZB_END, 11}, + {type::LZB_BLOCK_SIGN, 12}, + {type::ETCS_START, 13}, + {type::ETCS_END, 14}, + {type::ETCS_BLOCK_SIGN, 15}, + {type::SPEED_LIMIT, 16}, + {type::FORCED_HALT, 17}, + {type::POINT_SPEED, 18}, + {type::BRAKE_PATH, 19}, + {type::TRACK_NAME, 20}, + {type::LEVEL_CROSSING, 21}, }; auto const check_order = [&](struct section const& sec) { From b13dbf3f870c8fc7e6ff70fa7c47fba0413240bd Mon Sep 17 00:00:00 2001 From: julianharbarth Date: Wed, 25 Jan 2023 13:23:59 +0100 Subject: [PATCH 05/51] clang tidy --- src/infrastructure/interlocking/exclusion2.cc | 62 ++++++++++++------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/infrastructure/interlocking/exclusion2.cc b/src/infrastructure/interlocking/exclusion2.cc index bda2cc4d..3ccd33db 100644 --- a/src/infrastructure/interlocking/exclusion2.cc +++ b/src/infrastructure/interlocking/exclusion2.cc @@ -47,7 +47,7 @@ exclusion_materials get_exclusion_materials(infrastructure const& infra) { exclusion_materials mats(infra); - for (auto const& [ir_source_node, starting_at] : + for (auto const [ir_source_node, starting_at] : utl::enumerate(infra->interlocking_.starting_at_)) { if (starting_at.empty()) { @@ -180,11 +180,16 @@ soro::vector get_interlocking_exclusion_sets( std::cout << "working sets before\n"; std::cout << "non empty: " << non_empty << '\n'; std::cout << "avg diff: " << size / non_empty << '\n'; - std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; - std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; - std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; - std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; - std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + std::cout << "25%: " << sizes[static_cast(sizes.size()) * 0.25] + << '\n'; + std::cout << "50%: " << sizes[static_cast(sizes.size()) * 0.5] + << '\n'; + std::cout << "75%: " << sizes[static_cast(sizes.size()) * 0.75] + << '\n'; + std::cout << "90%: " << sizes[static_cast(sizes.size()) * 0.9] + << '\n'; + std::cout << "99%: " << sizes[static_cast(sizes.size()) * 0.99] + << '\n'; } clean_up_working_sets(working_sets); @@ -208,11 +213,16 @@ soro::vector get_interlocking_exclusion_sets( std::cout << "working sets after\n"; std::cout << "non empty: " << non_empty << '\n'; std::cout << "avg diff: " << size / non_empty << '\n'; - std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; - std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; - std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; - std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; - std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + std::cout << "25%: " << sizes[static_cast(sizes.size()) * 0.25] + << '\n'; + std::cout << "50%: " << sizes[static_cast(sizes.size()) * 0.5] + << '\n'; + std::cout << "75%: " << sizes[static_cast(sizes.size()) * 0.75] + << '\n'; + std::cout << "90%: " << sizes[static_cast(sizes.size()) * 0.9] + << '\n'; + std::cout << "99%: " << sizes[static_cast(sizes.size()) * 0.99] + << '\n'; } { @@ -234,11 +244,16 @@ soro::vector get_interlocking_exclusion_sets( std::cout << "working sets\n"; std::cout << "non empty: " << non_empty << '\n'; std::cout << "avg diff: " << size / non_empty << '\n'; - std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; - std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; - std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; - std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; - std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + std::cout << "25%: " << sizes[static_cast(sizes.size()) * 0.25] + << '\n'; + std::cout << "50%: " << sizes[static_cast(sizes.size()) * 0.5] + << '\n'; + std::cout << "75%: " << sizes[static_cast(sizes.size()) * 0.75] + << '\n'; + std::cout << "90%: " << sizes[static_cast(sizes.size()) * 0.9] + << '\n'; + std::cout << "99%: " << sizes[static_cast(sizes.size()) * 0.99] + << '\n'; } { @@ -260,11 +275,16 @@ soro::vector get_interlocking_exclusion_sets( std::cout << "path working sets\n"; std::cout << "non empty: " << non_empty << '\n'; std::cout << "avg diff: " << size / non_empty << '\n'; - std::cout << "25%: " << sizes[sizes.size() * 0.25] << '\n'; - std::cout << "50%: " << sizes[sizes.size() * 0.5] << '\n'; - std::cout << "75%: " << sizes[sizes.size() * 0.75] << '\n'; - std::cout << "90%: " << sizes[sizes.size() * 0.9] << '\n'; - std::cout << "99%: " << sizes[sizes.size() * 0.99] << '\n'; + std::cout << "25%: " << sizes[static_cast(sizes.size()) * 0.25] + << '\n'; + std::cout << "50%: " << sizes[static_cast(sizes.size()) * 0.5] + << '\n'; + std::cout << "75%: " << sizes[static_cast(sizes.size()) * 0.75] + << '\n'; + std::cout << "90%: " << sizes[static_cast(sizes.size()) * 0.9] + << '\n'; + std::cout << "99%: " << sizes[static_cast(sizes.size()) * 0.99] + << '\n'; } // utl::erase_if(sets, [](auto&& s) { return s.empty(); }); From 3fc558ff388fc5d508bf444247be4d8a60b5f4e1 Mon Sep 17 00:00:00 2001 From: julianharbarth Date: Wed, 1 Mar 2023 15:44:34 +0100 Subject: [PATCH 06/51] wip --- .pkg | 14 +- CMakeLists.txt | 228 +++---- cmake/pkg.cmake | 62 +- include/soro/base/soro_types.h | 16 +- .../soro/infrastructure/exclusion/exclusion.h | 26 + .../exclusion/exclusion_elements.h | 14 + .../exclusion/exclusion_graph.h | 11 + .../infrastructure/exclusion/exclusion_set.h | 111 ++++ .../infrastructure/exclusion/get_exclusion.h | 11 + .../exclusion/get_exclusion_graph.h | 13 + .../exclusion/get_exclusion_sets.h | 12 + .../infrastructure/exclusion/read_cliques.h | 11 + include/soro/infrastructure/graph/element.h | 9 + .../soro/infrastructure/graph/element_data.h | 3 +- include/soro/infrastructure/graph/node.h | 6 +- include/soro/infrastructure/graph/section.h | 33 + include/soro/infrastructure/graph/type.h | 1 + include/soro/infrastructure/infrastructure.h | 2 + .../infrastructure/infrastructure_options.h | 34 +- .../soro/infrastructure/infrastructure_t.h | 8 +- .../infrastructure/interlocking/exclusion.h | 56 -- .../infrastructure/interlocking/exclusion2.h | 98 --- .../interlocking/get_interlocking.h | 10 + .../interlocking/get_interlocking_subsystem.h | 11 - .../interlocking/interlocking.h | 25 + .../interlocking/interlocking_route.h | 8 +- .../interlocking/interlocking_subsystem.h | 18 - include/soro/infrastructure/station/station.h | 2 +- .../infrastructure/station/station_route.h | 9 +- include/soro/runtime/interval.h | 4 +- include/soro/timetable/sequence_point.h | 2 +- include/soro/utls/algo/multi_set_merge.h | 68 ++ include/soro/utls/container/it_range.h | 2 +- include/soro/utls/container/optional.h | 91 +-- include/soro/utls/coroutine/coro_namespace.h | 15 + include/soro/utls/coroutine/generator.h | 14 +- include/soro/utls/coroutine/iterator.h | 12 +- .../soro/utls/coroutine/recursive_generator.h | 12 - include/soro/utls/ranges/to.h | 32 + include/soro/utls/sassert.h | 8 +- include/soro/utls/serializable.h | 32 +- include/soro/utls/std_wrapper/std_wrapper.h | 7 + .../exclusion/exclusion_elements.cc | 173 ++++++ src/infrastructure/exclusion/exclusion_set.cc | 442 +++++++++++++ src/infrastructure/exclusion/get_exclusion.cc | 126 ++++ .../exclusion/get_exclusion_graph.cc | 63 ++ .../exclusion/get_exclusion_sets.cc | 493 +++++++++++++++ src/infrastructure/exclusion/read_cliques.cc | 34 + src/infrastructure/graph/element.cc | 4 + src/infrastructure/graph/graph_creation.cc | 4 +- src/infrastructure/graph/type.cc | 1 + src/infrastructure/interlocking/exclusion.cc | 409 ------------ src/infrastructure/interlocking/exclusion2.cc | 295 --------- ...cking_subsystem.cc => get_interlocking.cc} | 143 +++-- .../interlocking/interlocking_route.cc | 47 +- src/infrastructure/parsers/iss/parse_iss.cc | 95 ++- .../parsers/iss/parse_track_element.cc | 1 - src/infrastructure/regulatory_data.cc | 6 +- src/infrastructure/station/station_route.cc | 11 +- src/runtime/interval.cc | 12 +- .../station_route_to_interlocking_route.cc | 5 +- src/timetable/sequence_point.cc | 6 +- src/timetable/train.cc | 8 +- test/include/test/file_paths.h.in | 8 +- .../infrastructure/exclusion_graph_test.cc | 27 + test/src/infrastructure/exclusion_set_test.cc | 581 ++++++++++++++++++ test/src/infrastructure/graph_test.cc | 1 + .../src/infrastructure/infrastructure_test.cc | 2 +- .../interlocking_exclusion_test.cc | 46 -- .../infrastructure/interlocking_route_test.cc | 22 - test/src/infrastructure/section_test.cc | 26 + test/src/utls/algo/multi_set_merge_test.cc | 149 +++++ test/src/utls/container/optional_test.cc | 26 +- tools/export_exclusion_graph/CMakeLists.txt | 9 + .../src/export_exclusion_graph.cc | 79 +++ web/CMakeLists.txt | 0 .../exclusion/ExclusionComponent.js | 125 ++++ .../exclusion/exclusion_component.html | 54 ++ .../infrastructure/InfrastructureComponent.js | 91 ++- .../components/infrastructure/map/addIcons.js | 20 +- .../infrastructure/map/infrastructureMap.js | 377 ++++++------ .../components/infrastructure/map/mapStyle.js | 269 ++++---- .../station_detail/stationDetail.js | 121 ++-- .../station_detail/station_detail.html | 2 +- web/client/index.html | 409 ++++++------ web/client/model/InfrastructureManager.js | 88 +-- web/client/style/collapsible.css | 2 +- web/client/style/style.css | 31 +- web/server/CMakeLists.txt | 5 +- .../soro/server/cereal/cereal_extern.h | 58 ++ .../include/soro/server/cereal/json_archive.h | 73 +++ web/server/include/soro/server/http_server.h | 41 -- .../modules/infrastructure/infrastructure.h | 54 ++ .../{ => modules/tiles}/import/import.h | 0 .../tiles}/osm_export/interpolation.h | 0 .../tiles}/osm_export/osm_export.h | 2 +- .../tiles}/osm_export/osm_tag_names.h | 0 .../include/soro/server/modules/tiles/tiles.h | 40 ++ .../include/soro/server/server_settings.h | 58 ++ web/server/include/soro/server/soro_server.h | 45 +- web/server/src/http_server.cc | 65 -- web/server/src/main.cc | 52 ++ .../modules/infrastructure/infrastructure.cc | 79 +++ .../infrastructure/serve_exclusion_set.cc | 34 + .../infrastructure/serve_exclusion_sets.cc | 36 ++ .../serve_infrastructure_names.cc | 20 + .../serve_interlocking_route.cc | 45 ++ .../modules/infrastructure/serve_station.cc | 65 ++ .../infrastructure/serve_station_names.cc | 34 + .../infrastructure/serve_station_route.cc | 43 ++ .../src/{ => modules/tiles}/import/import.cc | 21 +- .../tiles}/osm_export/interpolation.cc | 2 +- .../tiles}/osm_export/osm_export.cc | 11 +- web/server/src/modules/tiles/tile_state.cc | 100 +++ web/server/src/server.cc | 132 ---- web/server/src/soro_server.cc | 360 ++++------- web/server/test/src/osm_export_test.cc | 2 +- web/server/test/src/way_interpolation_test.cc | 2 +- 118 files changed, 4950 insertions(+), 2633 deletions(-) create mode 100644 include/soro/infrastructure/exclusion/exclusion.h create mode 100644 include/soro/infrastructure/exclusion/exclusion_elements.h create mode 100644 include/soro/infrastructure/exclusion/exclusion_graph.h create mode 100644 include/soro/infrastructure/exclusion/exclusion_set.h create mode 100644 include/soro/infrastructure/exclusion/get_exclusion.h create mode 100644 include/soro/infrastructure/exclusion/get_exclusion_graph.h create mode 100644 include/soro/infrastructure/exclusion/get_exclusion_sets.h create mode 100644 include/soro/infrastructure/exclusion/read_cliques.h delete mode 100644 include/soro/infrastructure/interlocking/exclusion.h delete mode 100644 include/soro/infrastructure/interlocking/exclusion2.h create mode 100644 include/soro/infrastructure/interlocking/get_interlocking.h delete mode 100644 include/soro/infrastructure/interlocking/get_interlocking_subsystem.h create mode 100644 include/soro/infrastructure/interlocking/interlocking.h delete mode 100644 include/soro/infrastructure/interlocking/interlocking_subsystem.h create mode 100644 include/soro/utls/algo/multi_set_merge.h create mode 100644 include/soro/utls/coroutine/coro_namespace.h create mode 100644 include/soro/utls/ranges/to.h create mode 100644 src/infrastructure/exclusion/exclusion_elements.cc create mode 100644 src/infrastructure/exclusion/exclusion_set.cc create mode 100644 src/infrastructure/exclusion/get_exclusion.cc create mode 100644 src/infrastructure/exclusion/get_exclusion_graph.cc create mode 100644 src/infrastructure/exclusion/get_exclusion_sets.cc create mode 100644 src/infrastructure/exclusion/read_cliques.cc delete mode 100644 src/infrastructure/interlocking/exclusion.cc delete mode 100644 src/infrastructure/interlocking/exclusion2.cc rename src/infrastructure/interlocking/{get_interlocking_subsystem.cc => get_interlocking.cc} (66%) create mode 100644 test/src/infrastructure/exclusion_graph_test.cc create mode 100644 test/src/infrastructure/exclusion_set_test.cc delete mode 100644 test/src/infrastructure/interlocking_exclusion_test.cc create mode 100644 test/src/utls/algo/multi_set_merge_test.cc create mode 100644 tools/export_exclusion_graph/CMakeLists.txt create mode 100644 tools/export_exclusion_graph/src/export_exclusion_graph.cc delete mode 100644 web/CMakeLists.txt create mode 100644 web/client/components/exclusion/ExclusionComponent.js create mode 100644 web/client/components/exclusion/exclusion_component.html create mode 100644 web/server/include/soro/server/cereal/cereal_extern.h create mode 100644 web/server/include/soro/server/cereal/json_archive.h delete mode 100644 web/server/include/soro/server/http_server.h create mode 100644 web/server/include/soro/server/modules/infrastructure/infrastructure.h rename web/server/include/soro/server/{ => modules/tiles}/import/import.h (100%) rename web/server/include/soro/server/{ => modules/tiles}/osm_export/interpolation.h (100%) rename web/server/include/soro/server/{ => modules/tiles}/osm_export/osm_export.h (97%) rename web/server/include/soro/server/{ => modules/tiles}/osm_export/osm_tag_names.h (100%) create mode 100644 web/server/include/soro/server/modules/tiles/tiles.h create mode 100644 web/server/include/soro/server/server_settings.h delete mode 100644 web/server/src/http_server.cc create mode 100644 web/server/src/main.cc create mode 100644 web/server/src/modules/infrastructure/infrastructure.cc create mode 100644 web/server/src/modules/infrastructure/serve_exclusion_set.cc create mode 100644 web/server/src/modules/infrastructure/serve_exclusion_sets.cc create mode 100644 web/server/src/modules/infrastructure/serve_infrastructure_names.cc create mode 100644 web/server/src/modules/infrastructure/serve_interlocking_route.cc create mode 100644 web/server/src/modules/infrastructure/serve_station.cc create mode 100644 web/server/src/modules/infrastructure/serve_station_names.cc create mode 100644 web/server/src/modules/infrastructure/serve_station_route.cc rename web/server/src/{ => modules/tiles}/import/import.cc (68%) rename web/server/src/{ => modules/tiles}/osm_export/interpolation.cc (98%) rename web/server/src/{ => modules/tiles}/osm_export/osm_export.cc (96%) create mode 100644 web/server/src/modules/tiles/tile_state.cc delete mode 100644 web/server/src/server.cc diff --git a/.pkg b/.pkg index 42e27812..6c2220fe 100644 --- a/.pkg +++ b/.pkg @@ -1,7 +1,7 @@ [cista] url=git@github.com:felixguendling/cista.git branch=master - commit=1ab98b3a3b9b41b12788107325df3d7b7c49efa6 + commit=7d56bbdb0f653acdb04f21022e8071d8f67e0c66 [utl] url=git@github.com:motis-project/utl.git branch=master @@ -34,3 +34,15 @@ url=git@github.com:motis-project/boost.git branch=master commit=c90d53bdcd7ff741a416ae122b33c9c2a96e8be7 +[net] + url=git@github.com:motis-project/net.git + branch=master + commit=47a699159ea87e781451ce80e09915e2a8278082 +[range-v3] + url=git@github.com:motis-project/range-v3.git + branch=master + commit=4178d8359d76e3ba612d40a980bc0e5711073ca8 +[cereal] + url=git@github.com:motis-project/cereal.git + branch=sync2 + commit=58de6dab04d54833332ef70b05047b3d8c49f0c2 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e1bd0193..0835d22b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,50 +8,52 @@ project(soro CXX) option(SERIALIZE "Enable serialization with cista." OFF) option(USE_CISTA_RAW "Use cista::raw instead of cista::offset." OFF) -if (SERIALIZE) - set(SORO_COMPILE_DEFINITIONS SERIALIZE ${SORO_COMPILE_DEFINITIONS}) -else () - set(SORO_COMPILE_DEFINITIONS STD ${SORO_COMPILE_DEFINITIONS}) -endif () - -if (USE_CISTA_RAW) - set(SORO_COMPILE_DEFINITIONS USE_CISTA_RAW ${SORO_COMPILE_DEFINITIONS}) -endif () +if(SERIALIZE) + set(SORO_COMPILE_DEFINITIONS SERIALIZE ${SORO_COMPILE_DEFINITIONS}) + # rename the cereal serialization function to avoid naming conflicts with cista + set(SORO_COMPILE_DEFINITIONS CEREAL_SERIALIZE_FUNCTION_NAME=cereal_serialize ${SORO_COMPILE_DEFINITIONS}) +else() + set(SORO_COMPILE_DEFINITIONS STD ${SORO_COMPILE_DEFINITIONS}) +endif() + +if(USE_CISTA_RAW) + set(SORO_COMPILE_DEFINITIONS USE_CISTA_RAW ${SORO_COMPILE_DEFINITIONS}) +endif() # --- CLANG SUITE OPTIONS --- # option(SORO_SAN "Run clang with sanitizers." OFF) option(SORO_LINT "Run clang-tidy with the compiler." OFF) -if (SORO_SAN) - SET(SORO_COMPILE_DEFINITIONS SORO_SAN ${SORO_COMPILE_DEFINITIONS}) - SET(SORO_LINK_STATIC "") -else () - SET(SORO_LINK_STATIC "-static") -endif () +if(SORO_SAN) + SET(SORO_COMPILE_DEFINITIONS SORO_SAN ${SORO_COMPILE_DEFINITIONS}) + SET(SORO_LINK_STATIC "") +else() + SET(SORO_LINK_STATIC "-static") +endif() -if (SORO_LINT) - include(cmake/clang-tidy.cmake) -endif () +if(SORO_LINT) + include(cmake/clang-tidy.cmake) +endif() # --- MISC OPTIONS --- # option(SORO_DEBUG "Build the code with debug information." OFF) option(SORO_CUDA "Enable GPU-accelerated computing." OFF) -if (SORO_DEBUG) - set(SORO_COMPILE_DEFINITIONS SORO_DEBUG ${SORO_COMPILE_DEFINITIONS}) -endif () +if(SORO_DEBUG) + set(SORO_COMPILE_DEFINITIONS SORO_DEBUG ${SORO_COMPILE_DEFINITIONS}) +endif() -if (SORO_CUDA) - set(SORO_COMPILE_DEFINITIONS SORO_CUDA ${SORO_COMPILE_DEFINITIONS}) +if(SORO_CUDA) + set(SORO_COMPILE_DEFINITIONS SORO_CUDA ${SORO_COMPILE_DEFINITIONS}) - set(SORO_CUDA_ARCH "-gencode arch=compute_75,code=sm_75 -gencode arch=compute_61,code=sm_61") - if (MSVC) - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${SORO_CUDA_ARCH} -lcudadevrt") - else () - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${SORO_CUDA_ARCH} --compiler-options -static") - endif () - enable_language(CUDA) -endif () + set(SORO_CUDA_ARCH "-gencode arch=compute_75,code=sm_75 -gencode arch=compute_61,code=sm_61") + if(MSVC) + set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${SORO_CUDA_ARCH} -lcudadevrt") + else() + set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${SORO_CUDA_ARCH} --compiler-options -static") + endif() + enable_language(CUDA) +endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") @@ -69,52 +71,58 @@ find_package(Threads REQUIRED) ### --- CXX FLAGS --- ### set(SORO_COMPILE_OPTIONS "") -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} - -Weverything - -Wno-c++98-compat - -Wno-c++98-compat-pedantic - -Wno-newline-eof - -Wno-missing-prototypes - -Wno-padded - -Wno-double-promotion - -Wno-undef - -Wno-undefined-reinterpret-cast - -Wno-float-conversion - -Wno-global-constructors - -Wno-exit-time-destructors - -Wno-switch-enum - -Wno-c99-designator - -Wno-zero-as-null-pointer-constant - -Wno-missing-noreturn - -Wno-deprecated-experimental-coroutine - -Werror - -fcoroutines-ts - ${SORO_LINK_STATIC} - ) - - if (CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 15) - set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} - -Wno-deprecated-non-prototype) - endif () - - if (SORO_SAN) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined, -fno-omit-frame-pointer") - endif () -elseif (MSVC) +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-newline-eof + -Wno-missing-prototypes + -Wno-padded + -Wno-double-promotion + -Wno-undef + -Wno-undefined-reinterpret-cast + -Wno-float-conversion + -Wno-global-constructors + -Wno-exit-time-destructors + -Wno-switch-enum + -Wno-c99-designator + -Wno-zero-as-null-pointer-constant + -Wno-missing-noreturn + -Wno-deprecated-experimental-coroutine + -Werror + -fcoroutines-ts + -fexperimental-library + ${SORO_LINK_STATIC} + ) + + if(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 15) set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} - /WX - ) -else () + -Wno-deprecated-non-prototype) + endif() + + if(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 16) set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} - -Wall - -Wextra - -Werror - -fcoroutines - ${SORO_LINK_STATIC} - ) -endif () + -Wno-unsafe-buffer-usage) # TODO(julian) try enabling this + endif() + + if(SORO_SAN) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,undefined -fno-omit-frame-pointer") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,undefined, -fno-omit-frame-pointer") + endif() +elseif(MSVC) + set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} + /WX + ) +else() + set(SORO_COMPILE_OPTIONS ${SORO_COMPILE_OPTIONS} + -Wall + -Wextra + -Werror + -fcoroutines + ${SORO_LINK_STATIC} + ) +endif() set(SORO_COMPILE_FEATURES cxx_std_20) @@ -126,27 +134,27 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") file(GLOB_RECURSE soro-lib-files src/*.cc) add_library(soro-lib STATIC EXCLUDE_FROM_ALL ${soro-lib-files}) -if (SORO_CUDA) - add_library(infrastructure-cuda src/infrastructure/gpu/exclusion.cu) - set_target_properties(infrastructure-cuda PROPERTIES - WINDOWS_EXPORT_ALL_SYMBOLS ON - CUDA_STANDARD 20 - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} - LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} - INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/") - target_include_directories(infrastructure-cuda PUBLIC include) - set_property(TARGET infrastructure-cuda PROPERTY CUDA_ARCHITECTURES 75 61) -endif () +if(SORO_CUDA) + add_library(infrastructure-cuda src/infrastructure/gpu/exclusion.cu) + set_target_properties(infrastructure-cuda PROPERTIES + WINDOWS_EXPORT_ALL_SYMBOLS ON + CUDA_STANDARD 20 + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/") + target_include_directories(infrastructure-cuda PUBLIC include) + set_property(TARGET infrastructure-cuda PROPERTY CUDA_ARCHITECTURES 75 61) +endif() target_compile_options(soro-lib PRIVATE ${SORO_COMPILE_OPTIONS}) target_compile_features(soro-lib PRIVATE ${SORO_COMPILE_FEATURES}) target_compile_definitions(soro-lib PRIVATE ${SORO_COMPILE_DEFINITIONS}) target_include_directories(soro-lib PUBLIC include) -target_link_libraries(soro-lib PRIVATE utl cista date pugixml Threads::Threads) +target_link_libraries(soro-lib PUBLIC utl cista date pugixml range-v3 Threads::Threads) -if (SORO_CUDA) - target_link_libraries(soro-lib PUBLIC infrastructure-cuda) -endif () +if(SORO_CUDA) + target_link_libraries(soro-lib PUBLIC infrastructure-cuda) +endif() # Gets us the soro-server-client target set(SORO_SERVER_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -155,7 +163,7 @@ add_subdirectory(web/client) # Generate file_paths.h for locating the test resources configure_file(test/include/test/file_paths.h.in - ${CMAKE_CURRENT_SOURCE_DIR}/test/include/test/file_paths.h) + ${CMAKE_CURRENT_SOURCE_DIR}/test/include/test/file_paths.h) # Build the tests by getting all test files and linking to doctest and soro-lib file(GLOB_RECURSE soro-test-files test/src/*.cc web/server/test/src/*.cc) @@ -172,24 +180,26 @@ target_include_directories(soro-test PUBLIC test/include) # returns all targets except targets defined in deps/- function(get_all_targets _result _dir) - get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES) - foreach (_subdir IN LISTS _subdirs) - if (NOT ${_subdir} MATCHES ".*deps.*") - get_all_targets(${_result} "${_subdir}") - endif () - endforeach () - - get_directory_property(_sub_targets DIRECTORY "${_dir}" BUILDSYSTEM_TARGETS) - set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE) + get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES) + foreach(_subdir IN LISTS _subdirs) + if(NOT ${_subdir} MATCHES ".*deps.*") + get_all_targets(${_result} "${_subdir}") + endif() + endforeach() + + get_directory_property(_sub_targets DIRECTORY "${_dir}" BUILDSYSTEM_TARGETS) + set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE) endfunction() # if linting is on set the clang tidy property on all defined targets -if (SORO_LINT) - get_all_targets(all-targets ${CMAKE_CURRENT_SOURCE_DIR}) - - foreach (target ${all-targets}) - set_target_properties(${target} PROPERTIES - CXX_CLANG_TIDY ${SORO_CLANG_TIDY} - ) - endforeach () -endif () +if(SORO_LINT) + get_all_targets(all-targets ${CMAKE_CURRENT_SOURCE_DIR}) + + foreach(target ${all-targets}) + set_target_properties(${target} PROPERTIES + CXX_CLANG_TIDY ${SORO_CLANG_TIDY} + ) + endforeach() +endif() + +add_subdirectory(tools) \ No newline at end of file diff --git a/cmake/pkg.cmake b/cmake/pkg.cmake index 53ca3f2f..d253d015 100644 --- a/cmake/pkg.cmake +++ b/cmake/pkg.cmake @@ -1,41 +1,41 @@ set(pkg-bin "${CMAKE_BINARY_DIR}/dl/pkg") -if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - set(pkg-url "pkg") -elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - set(pkg-url "pkg.exe") -elseif (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") - set(pkg-url "pkgosx") -else () - message(STATUS "Not downloading pkg tool. Using pkg from PATH.") - set(pkg-bin "pkg") -endif () +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + set(pkg-url "pkg") +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(pkg-url "pkg.exe") +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + set(pkg-url "pkgosx") +else() + message(STATUS "Not downloading pkg tool. Using pkg from PATH.") + set(pkg-bin "pkg") +endif() -if (pkg-url) - if (NOT EXISTS ${pkg-bin}) - message(STATUS "Downloading pkg binary from https://github.com/motis-project/pkg/releases/latest/download/${pkg-url}") - file(DOWNLOAD "https://github.com/motis-project/pkg/releases/latest/download/${pkg-url}" ${pkg-bin}) - if (UNIX) - execute_process(COMMAND chmod +x ${pkg-bin}) - endif () - else () - message(STATUS "Pkg binary located in project.") - endif () -endif () +if(pkg-url) + if(NOT EXISTS ${pkg-bin}) + message(STATUS "Downloading pkg binary from https://github.com/motis-project/pkg/releases/latest/download/${pkg-url}") + file(DOWNLOAD "https://github.com/motis-project/pkg/releases/latest/download/${pkg-url}" ${pkg-bin}) + if(UNIX) + execute_process(COMMAND chmod +x ${pkg-bin}) + endif() + else() + message(STATUS "Pkg binary located in project.") + endif() +endif() -message(STATUS "${pkg-bin} -l -f") +message(STATUS "${pkg-bin} -l") execute_process( - COMMAND ${pkg-bin} -l -f -h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${pkg-bin} -l -h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) -if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/deps") - add_subdirectory(deps) -endif () +if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/deps") + add_subdirectory(deps) +endif() set_property( - DIRECTORY - APPEND - PROPERTY CMAKE_CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/.pkg" + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/.pkg" ) diff --git a/include/soro/base/soro_types.h b/include/soro/base/soro_types.h index 0955d743..7415dcf0 100644 --- a/include/soro/base/soro_types.h +++ b/include/soro/base/soro_types.h @@ -2,8 +2,10 @@ #include "cista/containers/array.h" #include "cista/containers/hash_set.h" +#include "cista/containers/optional.h" #include "cista/containers/variant.h" +#include "soro/utls/container/optional.h" #include "soro/utls/function_alias.h" #if defined(SERIALIZE) @@ -70,7 +72,16 @@ using array = data::array; template using variant = data::variant; +#if defined(__clang__) +template +using optional = utls::optional; +#else +template +using optional = cista::optional; +#endif + using string = data::string; + using string_view = data::string_view; template @@ -126,9 +137,10 @@ using array = data::array; template using variant = data::variant; -using string = std::string; +template +using optional = utls::optional; -inline const char* c_str(std::string const& s) { return s.c_str(); } +using string = std::string; template soro::unique_ptr make_unique(Args&&... args) { diff --git a/include/soro/infrastructure/exclusion/exclusion.h b/include/soro/infrastructure/exclusion/exclusion.h new file mode 100644 index 00000000..dbd82c4c --- /dev/null +++ b/include/soro/infrastructure/exclusion/exclusion.h @@ -0,0 +1,26 @@ +#pragma once + +#include "soro/infrastructure/exclusion/exclusion_graph.h" +#include "soro/infrastructure/exclusion/exclusion_set.h" +#include "soro/infrastructure/interlocking/interlocking_route.h" + +namespace soro::infra { + +struct exclusion { + // contains the exclusion elements for each interlocking route + // [start, ..., end], i.e. a closed interval + soro::vector closed_exclusion_elements_; + // contains the exclusion elements for each interlocking route, w/o start end + // (start, ..., end), i.e. an open interval + soro::vector open_exclusion_elements_; + + exclusion_graph exclusion_graph_; + + // all maximal cliques in the exclusion graph + soro::vector exclusion_sets_; + + // maps IRs to their cliques in the exclusion graph + soro::vector irs_to_exclusion_sets_; +}; + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/exclusion_elements.h b/include/soro/infrastructure/exclusion/exclusion_elements.h new file mode 100644 index 00000000..e8e732eb --- /dev/null +++ b/include/soro/infrastructure/exclusion/exclusion_elements.h @@ -0,0 +1,14 @@ +#pragma once + +#include "soro/infrastructure/infrastructure.h" + +namespace soro::infra { + +soro::vector get_closed_exclusion_elements( + infrastructure const& infra); + +soro::vector get_open_exclusion_elements( + soro::vector const& closed_exclusion_elements, + infrastructure const& infra); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/exclusion_graph.h b/include/soro/infrastructure/exclusion/exclusion_graph.h new file mode 100644 index 00000000..77b42ef7 --- /dev/null +++ b/include/soro/infrastructure/exclusion/exclusion_graph.h @@ -0,0 +1,11 @@ +#pragma once + +#include "soro/infrastructure/exclusion/exclusion_set.h" + +namespace soro::infra { + +struct exclusion_graph { + soro::vector nodes_; +}; + +} // namespace soro::infra \ No newline at end of file diff --git a/include/soro/infrastructure/exclusion/exclusion_set.h b/include/soro/infrastructure/exclusion/exclusion_set.h new file mode 100644 index 00000000..7defd078 --- /dev/null +++ b/include/soro/infrastructure/exclusion/exclusion_set.h @@ -0,0 +1,111 @@ +#pragma once + +#include "soro/base/soro_types.h" + +#include "cista/containers/bitvec.h" + +#include "soro/utls/sassert.h" +#include "soro/utls/std_wrapper/std_wrapper.h" + +namespace soro::infra { + +struct exclusion_set { + using value_type = uint32_t; + using id = uint32_t; + using bitvec_t = soro::data::bitvec; + + static constexpr auto INVALID_ID = std::numeric_limits::max(); + static constexpr auto INVALID_OFFSET = std::numeric_limits::max(); + + using optional_id = soro::optional; + using ids = soro::vector; + + struct iterator { + using iterator_category = typename std::input_iterator_tag; + using value_type = exclusion_set::value_type; + using difference_type = value_type; + using pointer = value_type*; + using reference = value_type; + + iterator(exclusion_set const* const set, bitvec_t::size_type const idx) + : set_{set}, idx_{idx} { + utls::expects(idx_ <= set_->bits_.size(), + "initializing idx larger than size"); + } + + iterator operator+(std::size_t n) { + auto result = *this; + + while (n != 0) { + ++result; + --n; + } + + return result; + } + + iterator& operator++() { + ++idx_; + while (idx_ < set_->bits_.size() && !set_->bits_[idx_]) { + ++idx_; + } + + return *this; + } + + bool operator==(iterator const& other) const = default; + bool operator!=(iterator const& other) const = default; + + value_type operator*() const { return set_->first_ + idx_; } + pointer operator->() = delete; + + private: + exclusion_set const* set_{nullptr}; + bitvec_t::size_type idx_{std::numeric_limits::max()}; + }; + + auto begin() const { return iterator{this, first_bit_set_ - first_}; } + auto end() const { return iterator{this, bits_.size()}; } + + soro::vector expanded_set() const; + + bool operator[](value_type const idx) const; + + void set(value_type const idx); + + exclusion_set& operator-=(exclusion_set const& other); + exclusion_set& operator|=(exclusion_set const& other); + + exclusion_set operator-(exclusion_set const& other) const; + exclusion_set operator|(exclusion_set const& other) const; + + std::partial_ordering compare(exclusion_set const& other) const; + + bool contains(exclusion_set const& other) const; + + bool operator==(exclusion_set const& o) const noexcept = default; + + value_type size() const; + std::size_t count() const; + + void clear(); + + bool any() const; + bool empty() const; + bool ok() const; + + id id_{INVALID_ID}; + + value_type first_{INVALID_OFFSET}; + value_type last_{INVALID_OFFSET}; + + value_type first_bit_set_{INVALID_OFFSET}; + value_type last_bit_set_{INVALID_OFFSET}; + + bitvec_t bits_{}; +}; + +exclusion_set make_exclusion_set( + soro::vector const& sorted_ids); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/get_exclusion.h b/include/soro/infrastructure/exclusion/get_exclusion.h new file mode 100644 index 00000000..5106455a --- /dev/null +++ b/include/soro/infrastructure/exclusion/get_exclusion.h @@ -0,0 +1,11 @@ +#pragma once + +#include "soro/infrastructure/exclusion/exclusion.h" +#include "soro/infrastructure/infrastructure_t.h" + +namespace soro::infra { + +exclusion get_exclusion(infrastructure_t const& infra_t, + option const exclusion_graph); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/get_exclusion_graph.h b/include/soro/infrastructure/exclusion/get_exclusion_graph.h new file mode 100644 index 00000000..aea1343b --- /dev/null +++ b/include/soro/infrastructure/exclusion/get_exclusion_graph.h @@ -0,0 +1,13 @@ +#pragma once + +#include "soro/infrastructure/exclusion/exclusion_graph.h" +#include "soro/infrastructure/infrastructure.h" + +namespace soro::infra { + +exclusion_graph get_exclusion_graph( + soro::vector const& closed_exclusion_elements, + soro::vector const& closed_element_used_by, + infrastructure const& infra); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/get_exclusion_sets.h b/include/soro/infrastructure/exclusion/get_exclusion_sets.h new file mode 100644 index 00000000..042fcfda --- /dev/null +++ b/include/soro/infrastructure/exclusion/get_exclusion_sets.h @@ -0,0 +1,12 @@ +#pragma once + +#include "soro/infrastructure/infrastructure.h" + +namespace soro::infra { + +soro::vector get_exclusion_sets( + infrastructure const& infra, + soro::vector const& closed_element_used_by, + soro::vector const& open_exclusion_elements); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/exclusion/read_cliques.h b/include/soro/infrastructure/exclusion/read_cliques.h new file mode 100644 index 00000000..4e012249 --- /dev/null +++ b/include/soro/infrastructure/exclusion/read_cliques.h @@ -0,0 +1,11 @@ +#pragma once + +#include "soro/infrastructure/exclusion/exclusion_set.h" +#include "soro/infrastructure/interlocking/interlocking_route.h" + +namespace soro::infra { + +soro::vector read_cliques( + std::filesystem::path const& clique_fp); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/graph/element.h b/include/soro/infrastructure/graph/element.h index 548f9e6b..055ed37d 100644 --- a/include/soro/infrastructure/graph/element.h +++ b/include/soro/infrastructure/graph/element.h @@ -315,6 +315,7 @@ struct element { #endif using ptr = element_ptr; + using ids = soro::vector; using member_t = soro::variant neighbours() { + return this->e_.apply([](auto&& e) -> std::span { + return {std::begin(e.neighbours_), e.neighbours_.size()}; + }); + } + std::span nodes() const { return this->e_.apply([](auto&& e) -> std::span { return {std::cbegin(e.nodes_), e.nodes_.size()}; @@ -379,6 +386,8 @@ struct element { bool is_switch() const; + bool joins_tracks() const; + kilometrage get_km(element::ptr neigh) const; si::length get_distance(element::ptr const neigh) const; node_ptr reverse_ahead(node_ptr n) const; diff --git a/include/soro/infrastructure/graph/element_data.h b/include/soro/infrastructure/graph/element_data.h index 3b98e085..220252b0 100644 --- a/include/soro/infrastructure/graph/element_data.h +++ b/include/soro/infrastructure/graph/element_data.h @@ -2,7 +2,6 @@ #include "soro/base/soro_types.h" #include "soro/si/units.h" -#include "soro/utls/container/optional.h" namespace soro::infra { @@ -50,7 +49,7 @@ struct halt { struct speed_limit { using ptr = soro::ptr; - using optional_ptr = utls::optional; + using optional_ptr = soro::optional; enum class type : uint8_t { END_SPECIAL, diff --git a/include/soro/infrastructure/graph/node.h b/include/soro/infrastructure/graph/node.h index b6cdfb16..2d95e87b 100644 --- a/include/soro/infrastructure/graph/node.h +++ b/include/soro/infrastructure/graph/node.h @@ -25,9 +25,9 @@ struct node { return idx != INVALID_IDX; } - using optional_id = utls::optional; - using optional_ptr = utls::optional; - using optional_idx = utls::optional; + using optional_id = soro::optional; + using optional_ptr = soro::optional; + using optional_idx = soro::optional; bool is(type const t) const; diff --git a/include/soro/infrastructure/graph/section.h b/include/soro/infrastructure/graph/section.h index e49285c5..85cebf62 100644 --- a/include/soro/infrastructure/graph/section.h +++ b/include/soro/infrastructure/graph/section.h @@ -18,6 +18,7 @@ enum class skip : bool { No, Yes }; struct section { using id = uint32_t; + using ids = soro::vector; template utls::generator iterate() const { @@ -36,6 +37,38 @@ struct section { } } + auto from(element::ptr const element, direction const dir) const { + auto const get_range = [&](auto const& v) { + auto const from = std::find(std::begin(v), std::end(v), element); + return std::span{from, std::end(v)}; + }; + + return dir == direction::Rising ? get_range(rising_order_) + : get_range(falling_order_); + } + + auto to(element::ptr const element, direction const dir) const { + auto const get_range = [&](auto const& v) { + auto const to = std::find(std::begin(v), std::end(v), element); + return std::span{std::begin(v), to + 1}; + }; + + return dir == direction::Rising ? get_range(rising_order_) + : get_range(falling_order_); + } + + auto from_to(element::ptr const from, element::ptr const to, + direction const dir) const { + auto const get_range = [&](auto const& v) { + auto const f = std::find(std::begin(v), std::end(v), from); + auto const t = std::find(std::begin(v), std::end(v), to); + return std::span{f, t + 1}; + }; + + return dir == direction::Rising ? get_range(rising_order_) + : get_range(falling_order_); + } + std::size_t size() const { return rising_order_.size(); } element::ptr first_rising() const { return rising_order_.front(); } diff --git a/include/soro/infrastructure/graph/type.h b/include/soro/infrastructure/graph/type.h index 6f06be48..d4cea53d 100644 --- a/include/soro/infrastructure/graph/type.h +++ b/include/soro/infrastructure/graph/type.h @@ -36,6 +36,7 @@ enum class type : type_id { ETCS_END, ETCS_BLOCK_SIGN, FORCED_HALT, + META, HALT, // undirected track elements TUNNEL, diff --git a/include/soro/infrastructure/infrastructure.h b/include/soro/infrastructure/infrastructure.h index 91d33f53..0c5e61c5 100644 --- a/include/soro/infrastructure/infrastructure.h +++ b/include/soro/infrastructure/infrastructure.h @@ -10,6 +10,8 @@ namespace soro::infra { struct infrastructure : utls::serializable { using utls::serializable::serializable; + using optional_ptr = soro::optional; + // TODO(julian) This wrapper is only a temporary solution explicit infrastructure(infrastructure_t const* wrapped_infra); diff --git a/include/soro/infrastructure/infrastructure_options.h b/include/soro/infrastructure/infrastructure_options.h index 8a0b18de..1c7eaf50 100644 --- a/include/soro/infrastructure/infrastructure_options.h +++ b/include/soro/infrastructure/infrastructure_options.h @@ -2,26 +2,40 @@ #include "cista/reflection/comparable.h" +#include "soro/infrastructure/exclusion/exclusion.h" +#include "soro/infrastructure/exclusion/exclusion_graph.h" +#include "soro/infrastructure/interlocking/interlocking.h" + #include namespace soro::infra { -// Parse an infrastructure from the following: -// - ISS 'Index.xml', which references all other ISS-Xid_ML files -// - ISS 'Spurplanbtrs.xml', which only contains station information -// - ISS 'infra.base_infrastructure', file, which is a .tar.zst file -// containing ISS-XML files -// - ISS 'example/', a folder which contains an 'Index.xml' +template +struct option { + option(bool const b) : b_{b} {} // NOLINT + operator bool() const { return b_; } // NOLINT + bool b_; +}; + +struct layout_tag; struct infrastructure_options { CISTA_COMPARABLE() - bool determine_interlocking_{true}; - bool determine_conflicts_{true}; - bool determine_layout_{true}; + option interlocking_{true}; + option exclusions_{true}; + option exclusion_graph_{false}; + option layout_{true}; - std::filesystem::path gps_coord_path_{""}; std::filesystem::path infrastructure_path_{""}; + std::filesystem::path gps_coord_path_{""}; }; +inline infrastructure_options make_infra_opts( + std::filesystem::path const& infrastructure_path, + std::filesystem::path const& coord_path) { + return infrastructure_options{.infrastructure_path_ = infrastructure_path, + .gps_coord_path_ = coord_path}; +} + } // namespace soro::infra \ No newline at end of file diff --git a/include/soro/infrastructure/infrastructure_t.h b/include/soro/infrastructure/infrastructure_t.h index 66846613..88e1b886 100644 --- a/include/soro/infrastructure/infrastructure_t.h +++ b/include/soro/infrastructure/infrastructure_t.h @@ -2,9 +2,10 @@ #include "soro/utls/coordinates/gps.h" +#include "soro/infrastructure/exclusion/exclusion.h" #include "soro/infrastructure/graph/graph.h" #include "soro/infrastructure/infrastructure_options.h" -#include "soro/infrastructure/interlocking/interlocking_subsystem.h" +#include "soro/infrastructure/interlocking/interlocking.h" #include "soro/infrastructure/line.h" #include "soro/infrastructure/station/station.h" #include "soro/infrastructure/station/station_route_graph.h" @@ -20,8 +21,6 @@ struct default_values { }; struct infrastructure_t { - using ptr = soro::ptr; - graph graph_; default_values defaults_; @@ -36,7 +35,8 @@ struct infrastructure_t { station_route_graph station_route_graph_{}; - interlocking_subsystem interlocking_{}; + interlocking interlocking_{}; + exclusion exclusion_{}; soro::vector full_station_names_{}; diff --git a/include/soro/infrastructure/interlocking/exclusion.h b/include/soro/infrastructure/interlocking/exclusion.h deleted file mode 100644 index 45de8d49..00000000 --- a/include/soro/infrastructure/interlocking/exclusion.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "soro/base/soro_types.h" - -#include "soro/infrastructure/interlocking/get_interlocking_subsystem.h" - -namespace soro::infra { - -/* - * Exclusion paths are used to partition the infrastructure into - * paths that can be used to compose station routes as well as interlocking - * routes. - * - * The reason for this model is that we can determine the exclusions w.r.t - * all exclusion paths and from them infer all station route and interlocking - * exclusions. - * - * Therefore, an exclusion path always starts and end on one of the following: - * - Main Signal - * - Border - * - Track End - * - Halt (if it is used by at least one station route as its start or end) - * - * Since we only need this model for determining the interlocking exclusions - * the nodes will be sorted. - */ -struct exclusion_path { - using id = uint32_t; - - static constexpr id INVALID = std::numeric_limits::max(); - - id id_{INVALID}; - - // contains all section elements and the head/tail track elements - // is sorted - std::vector elements_; -}; - -std::vector> get_exclusion_path_exclusions( - infrastructure const& infra); - -soro::vector> -get_interlocking_route_exclusions(interlocking_subsystem const& irs, - infrastructure_t const& infra); - -soro::vector get_exclusion_elements(interlocking_route const& ssr, - infrastructure_t const& iss); - -namespace detail { - -std::vector> get_station_route_exclusions( - infrastructure_t const& infra); - -} // namespace detail - -} // namespace soro::infra \ No newline at end of file diff --git a/include/soro/infrastructure/interlocking/exclusion2.h b/include/soro/infrastructure/interlocking/exclusion2.h deleted file mode 100644 index 9d27472b..00000000 --- a/include/soro/infrastructure/interlocking/exclusion2.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "soro/infrastructure/infrastructure.h" - -namespace soro::infra { - -struct exclusion_set { - using id = uint32_t; - using offset_t = soro::size_t; - using bitvec_t = cista::raw::bitvec; - - static constexpr auto INVALID_OFFSET = std::numeric_limits::max(); - - exclusion_set() = default; - - template - exclusion_set(std::vector const& sorted_ids) { - utls::sassert(utls::is_sorted(sorted_ids), "IDs not sorted."); - - if (sorted_ids.empty()) { - return; - } - - first_ = sorted_ids.front(); - last_ = sorted_ids.back() + 1; - bits_.resize(static_cast(last_ - first_)); - - for (auto const id : sorted_ids) { - bits_.set(static_cast(id - first_)); - } - } - - void set_first(offset_t const new_first) { - if (first_ == new_first) { - return; - } - - utls::sassert(new_first < first_); - - this->bits_.resize(bits_.size() + first_ - new_first); - this->bits_ <<= first_ - new_first; - - this->first_ = new_first; - } - - void set_last(offset_t const new_last) { - if (last_ == new_last) { - return; - } - - utls::sassert(new_last > last_); - this->bits_.resize(bits_.size() + new_last - last_); - this->last_ = new_last; - } - - bool contains(exclusion_set const& other) const { - if (this->empty()) { - return false; - } - - if (other.empty()) { - return true; - } - - utls::sassert(first_ == other.first_ && last_ == other.last_ && - bits_.size() == other.bits_.size()); - - for (auto i = 0U; i < bits_.size(); ++i) { - if (!this->bits_[i] && other.bits_[i]) { - return false; - } - } - - return true; - } - - bool operator==(exclusion_set const& o) const noexcept = default; - - void clear() { - first_ = INVALID_OFFSET; - last_ = INVALID_OFFSET; - bits_ = {}; - } - - bool empty() const { - return first_ == INVALID_OFFSET && last_ == INVALID_OFFSET && bits_.empty(); - } - - offset_t first_{INVALID_OFFSET}; - offset_t last_{INVALID_OFFSET}; - - bitvec_t bits_{}; -}; - -soro::vector get_interlocking_exclusion_sets( - infrastructure const& infra); - -} // namespace soro::infra diff --git a/include/soro/infrastructure/interlocking/get_interlocking.h b/include/soro/infrastructure/interlocking/get_interlocking.h new file mode 100644 index 00000000..ce3a54f2 --- /dev/null +++ b/include/soro/infrastructure/interlocking/get_interlocking.h @@ -0,0 +1,10 @@ +#pragma once + +#include "soro/infrastructure/infrastructure.h" +#include "soro/infrastructure/interlocking/interlocking.h" + +namespace soro::infra { + +interlocking get_interlocking(infrastructure_t const& infra); + +} // namespace soro::infra diff --git a/include/soro/infrastructure/interlocking/get_interlocking_subsystem.h b/include/soro/infrastructure/interlocking/get_interlocking_subsystem.h deleted file mode 100644 index 7711fe49..00000000 --- a/include/soro/infrastructure/interlocking/get_interlocking_subsystem.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "soro/infrastructure/infrastructure.h" -#include "soro/infrastructure/interlocking/interlocking_subsystem.h" - -namespace soro::infra { - -interlocking_subsystem get_interlocking_subsystem( - infrastructure_t const& infra, bool const determine_exclusions); - -} // namespace soro::infra diff --git a/include/soro/infrastructure/interlocking/interlocking.h b/include/soro/infrastructure/interlocking/interlocking.h new file mode 100644 index 00000000..a2d60023 --- /dev/null +++ b/include/soro/infrastructure/interlocking/interlocking.h @@ -0,0 +1,25 @@ +#pragma once + +#include "soro/infrastructure/interlocking/interlocking_route.h" + +namespace soro::infra { + +struct interlocking { + soro::vector routes_; + + // node n -> IRs starting at node n + soro::vector starting_at_; + // node n -> IRs ending at node n + soro::vector ending_at_; + + // node n -> IRs halting at node n + soro::vector halting_at_; + + soro::vector sr_to_participating_irs_; + soro::vector station_to_irs_; + + // deprecated + soro::vector exclusions_; +}; + +} // namespace soro::infra diff --git a/include/soro/infrastructure/interlocking/interlocking_route.h b/include/soro/infrastructure/interlocking/interlocking_route.h index a7f24fd1..2a57b66a 100644 --- a/include/soro/infrastructure/interlocking/interlocking_route.h +++ b/include/soro/infrastructure/interlocking/interlocking_route.h @@ -21,12 +21,14 @@ struct sub_path { struct interlocking_route { using id = uint32_t; + using ids = soro::vector; using ptr = soro::ptr; using sr_offset = uint8_t; static constexpr id INVALID = std::numeric_limits::max(); static constexpr bool valid(id const id) noexcept { return id != INVALID; } + bool static valid_end(type const t); type_set static valid_ends(); bool operator==(interlocking_route const& o) const; @@ -34,7 +36,7 @@ struct interlocking_route { bool follows(interlocking_route const& other, infrastructure const& infra) const; - std::vector elements(infrastructure const&) const; + std::vector elements(infrastructure const&) const; node::idx size(infrastructure const& infra) const; @@ -57,6 +59,9 @@ struct interlocking_route { bool starts_on_ms(infrastructure const&) const; bool ends_on_ms(infrastructure const&) const; + bool starts_on_section(infrastructure const&) const; + bool ends_on_section(infrastructure const&) const; + utls::recursive_generator from_to(station_route::id, node::idx, station_route::id, node::idx, infrastructure const&) const; @@ -83,6 +88,5 @@ struct interlocking_route { // shorthand aliases using ir_id = interlocking_route::id; -using ir_ptr = interlocking_route::ptr; } // namespace soro::infra diff --git a/include/soro/infrastructure/interlocking/interlocking_subsystem.h b/include/soro/infrastructure/interlocking/interlocking_subsystem.h deleted file mode 100644 index 2f3ae98f..00000000 --- a/include/soro/infrastructure/interlocking/interlocking_subsystem.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "soro/infrastructure/interlocking/interlocking_route.h" - -namespace soro::infra { - -struct interlocking_subsystem { - soro::vector routes_; - - soro::vector> starting_at_; - soro::vector> halting_at_; - - soro::vector> sr_to_participating_irs_; - soro::vector> station_to_irs_; - soro::vector> exclusions_; -}; - -} // namespace soro::infra diff --git a/include/soro/infrastructure/station/station.h b/include/soro/infrastructure/station/station.h index 74d64b4f..9b3e794e 100644 --- a/include/soro/infrastructure/station/station.h +++ b/include/soro/infrastructure/station/station.h @@ -20,7 +20,7 @@ struct station { static constexpr id INVALID = std::numeric_limits::max(); static constexpr bool valid(id const id) noexcept { return id != INVALID; } - using optional_ptr = utls::optional; + using optional_ptr = soro::optional; soro::vector neighbours() const; diff --git a/include/soro/infrastructure/station/station_route.h b/include/soro/infrastructure/station/station_route.h index 084fe23e..bfde4c54 100644 --- a/include/soro/infrastructure/station/station_route.h +++ b/include/soro/infrastructure/station/station_route.h @@ -1,8 +1,5 @@ #pragma once -#include - -#include "soro/utls/container/optional.h" #include "soro/utls/coroutine/recursive_generator.h" #include "soro/infrastructure/graph/element_data.h" @@ -23,7 +20,7 @@ struct route_node { bool omitted() const { return omitted_; } node::ptr node_{nullptr}; - speed_limit::optional_ptr extra_spl_{}; + speed_limit::optional_ptr extra_spl_; bool omitted_{false}; }; @@ -97,8 +94,8 @@ struct station_route { node::optional_idx runtime_checkpoint_{}; soro::ptr station_{nullptr}; - utls::optional from_station_{}; - utls::optional to_station_{}; + soro::optional from_station_{}; + soro::optional to_station_{}; si::length length_{si::INVALID}; diff --git a/include/soro/runtime/interval.h b/include/soro/runtime/interval.h index a19fa97e..36504f80 100644 --- a/include/soro/runtime/interval.h +++ b/include/soro/runtime/interval.h @@ -25,7 +25,9 @@ struct interval { : distance_(dist), limit_left_{left_limit}, limit_right_{right_limit}, - sequence_point_{sp}, + sequence_point_{sp.has_value() + ? tt::sequence_point::optional_ptr(*sp) + : tt::sequence_point::optional_ptr(std::nullopt)}, events_{std::move(events)} {} void append(interval const& o) { diff --git a/include/soro/timetable/sequence_point.h b/include/soro/timetable/sequence_point.h index 21a32388..60ca320f 100644 --- a/include/soro/timetable/sequence_point.h +++ b/include/soro/timetable/sequence_point.h @@ -12,7 +12,7 @@ struct sequence_point { using ptr = soro::ptr; - using optional_ptr = utls::optional; + using optional_ptr = soro::optional; enum struct type : uint8_t { TRANSIT, OPERATIONS, PASSENGER, REQUEST }; diff --git a/include/soro/utls/algo/multi_set_merge.h b/include/soro/utls/algo/multi_set_merge.h new file mode 100644 index 00000000..6d991d49 --- /dev/null +++ b/include/soro/utls/algo/multi_set_merge.h @@ -0,0 +1,68 @@ +#pragma once + +#include + +#include "utl/erase_if.h" + +#include "soro/base/soro_types.h" + +namespace soro::utls { + +// merges multiple sets into a single one +// requires the sets to be sorted +template +Result multi_set_merge(soro::vector> ranges) { + using range = std::pair; + + // remove empty ranges + utl::erase_if(ranges, [](auto&& r) { return r.first == r.second; }); + + if (ranges.empty()) { + return {}; + } + + if (ranges.size() == 1) { + return Result{ranges.front().first, ranges.front().second}; + } + + Result result; + + if constexpr (std::contiguous_iterator) { + auto const max = std::max_element( + std::begin(ranges), std::end(ranges), [](auto&& r1, auto&& r2) { + return std::distance(r1.first, r1.second) < + std::distance(r2.first, r2.second); + }); + + result.reserve(static_cast( + distance(max->first, max->second))); + } + + auto const cmp = [&](auto&& r1, auto&& r2) { return *r1.first > *r2.first; }; + std::priority_queue, decltype(cmp)> queue( + std::begin(ranges), std::end(ranges), cmp); + + // we don't want to check if result is empty later on + result.emplace_back(*queue.top().first); + + while (!queue.empty()) { + auto current = queue.top(); + queue.pop(); + + if (*current.first > result.back()) { + result.emplace_back(*current.first); + } + + while (current.first != current.second && *current.first <= result.back()) { + ++(current.first); + } + + if (current.first != current.second) { + queue.emplace(current); + } + } + + return result; +} + +} // namespace soro::utls diff --git a/include/soro/utls/container/it_range.h b/include/soro/utls/container/it_range.h index 4c513396..c410fcf8 100644 --- a/include/soro/utls/container/it_range.h +++ b/include/soro/utls/container/it_range.h @@ -19,7 +19,7 @@ struct it_range { }; template -constexpr auto make_range(Iterator begin, Iterator end) noexcept { +constexpr it_range make_range(Iterator begin, Iterator end) noexcept { return it_range(begin, end); } diff --git a/include/soro/utls/container/optional.h b/include/soro/utls/container/optional.h index 46a759cf..05a92e84 100644 --- a/include/soro/utls/container/optional.h +++ b/include/soro/utls/container/optional.h @@ -7,76 +7,78 @@ #include "cista/serialization.h" -#include "soro/base/soro_types.h" - namespace soro::utls { -template - requires std::is_integral_v || soro::is_pointer_v -struct optional { - using value_type = T; - static const constexpr auto INVALID_VALUE = INVALID; +template +constexpr T get_default_invalid() { + if constexpr (cista::is_pointer_v) { + return T{nullptr}; + } - constexpr optional() noexcept = default; - constexpr optional(T const val) noexcept : val_{val} {} // NOLINT + if constexpr (std::is_integral_v) { + return std::numeric_limits::max(); + } +} - constexpr optional(optional const& other) = default; - constexpr optional& operator=(optional const&) = default; +template ()> + requires std::is_integral_v || cista::is_pointer_v +struct optional { + optional() noexcept = default; + explicit optional(std::nullopt_t) {} + explicit optional(T const val) noexcept : val_{val} {} // NOLINT - constexpr optional(optional&& other) noexcept = default; - constexpr optional& operator=(optional&&) noexcept = default; + optional(optional const&) = default; + optional& operator=(optional const&) = default; - constexpr ~optional() = default; + optional(optional&&) noexcept = default; + optional& operator=(optional&&) noexcept = default; - constexpr optional& operator=(T const val) noexcept { - this->val_ = val; - return *this; - } + ~optional() = default; - constexpr T const* operator->() const noexcept { + T const* operator->() const noexcept { assert(has_value()); return &val_; } - constexpr T* operator->() noexcept { + T* operator->() noexcept { assert(has_value()); return &val_; } - constexpr T const& operator*() const& noexcept { return value(); } + T const& operator*() const& noexcept { return value(); } - constexpr T& operator*() & noexcept { return value(); } + T& operator*() & noexcept { return value(); } - constexpr T const&& operator*() const&& noexcept { return value(); } + T const&& operator*() const&& noexcept { return value(); } - constexpr T& operator*() && noexcept { return value(); } + T& operator*() && noexcept { return value(); } - constexpr explicit operator bool() const noexcept { return has_value(); } + explicit operator bool() const noexcept { return has_value(); } - constexpr bool has_value() const noexcept { return val_ != INVALID; } + bool has_value() const noexcept { return val_ != INVALID; } - constexpr T& value() & { + T& value() & { assert(has_value()); return val_; } - constexpr T const& value() const& { + T const& value() const& { assert(has_value()); return val_; } - constexpr T& value() && { + T& value() && { assert(has_value()); return val_; } - constexpr T const& value() const&& { + T const& value() const&& { assert(has_value()); return val_; } template - constexpr T value_or(U&& default_value) const& noexcept { + T value_or(U&& default_value) const& noexcept { if (has_value()) { return val_; } else { @@ -85,21 +87,21 @@ struct optional { } template - constexpr T value_or(U&& default_value) && noexcept { + T value_or(U&& default_value) && noexcept { return value_or(default_value); } - constexpr void reset() noexcept { val_ = INVALID_VALUE; } + void reset() noexcept { val_ = INVALID; } template - constexpr void execute_if(Fn&& fn) const noexcept { + void execute_if(Fn&& fn) const noexcept { if (has_value()) { fn(value()); } } template - constexpr auto and_then(ThenFunction&& then_function) const noexcept { + auto and_then(ThenFunction&& then_function) const noexcept { if (has_value()) { return then_function(value()); } else { @@ -120,16 +122,16 @@ struct optional { } template - constexpr optional or_else(ElseFunction&& else_function) const& noexcept { + optional or_else(ElseFunction&& else_function) const& noexcept { return has_value() ? *this : std::forward(else_function)(); } template - constexpr optional or_else(ElseFunction&& else_function) && noexcept { + optional or_else(ElseFunction&& else_function) && noexcept { return or_else(else_function); } - constexpr void swap(optional& other) noexcept { + void swap(optional& other) noexcept { if (has_value() && !other.has_value()) { other = this->val_; this->reset(); @@ -146,9 +148,12 @@ struct optional { #if !defined(SERIALIZE) private: #endif - T val_{INVALID_VALUE}; + T val_{INVALID}; }; +template +optional(T) -> optional; + template requires std::is_integral_v || std::is_pointer_v optional make_optional(T&& value) { @@ -174,4 +179,12 @@ inline void deserialize(Ctx const& context, optional* opt) { deserialize(context, &opt->val_); } +template +cista::hash_t type_hash(optional const&, cista::hash_t h, + std::map& done) noexcept { + h = cista::hash_combine(h, cista::hash("soro::utls::optional")); + h = cista::type_hash(T{}, h, done); + return h; +} + } // namespace soro::utls \ No newline at end of file diff --git a/include/soro/utls/coroutine/coro_namespace.h b/include/soro/utls/coroutine/coro_namespace.h new file mode 100644 index 00000000..9db7956f --- /dev/null +++ b/include/soro/utls/coroutine/coro_namespace.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 150000 +#include +#else +#include +#endif + +#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 150000 +namespace coro = std::experimental; +#else +namespace coro = std; +#endif diff --git a/include/soro/utls/coroutine/generator.h b/include/soro/utls/coroutine/generator.h index 867cefed..9b41d6e8 100644 --- a/include/soro/utls/coroutine/generator.h +++ b/include/soro/utls/coroutine/generator.h @@ -9,25 +9,15 @@ #pragma once -#if defined(__clang__) -#include -#else -#include -#endif - #include #include #include #include #include -namespace soro::utls { +#include "soro/utls/coroutine/coro_namespace.h" -#if defined(__clang__) -namespace coro = std::experimental; -#else -namespace coro = std; -#endif +namespace soro::utls { template struct generator; diff --git a/include/soro/utls/coroutine/iterator.h b/include/soro/utls/coroutine/iterator.h index 3024af89..6b6592f6 100644 --- a/include/soro/utls/coroutine/iterator.h +++ b/include/soro/utls/coroutine/iterator.h @@ -1,19 +1,9 @@ #pragma once -#if defined(__clang__) -#include -#else -#include -#endif +#include "soro/utls/coroutine/coro_namespace.h" namespace soro::utls { -#if defined(__clang__) -namespace coro = std::experimental; -#else -namespace coro = std; -#endif - namespace detail { template diff --git a/include/soro/utls/coroutine/recursive_generator.h b/include/soro/utls/coroutine/recursive_generator.h index b3d34df9..b08d48a7 100644 --- a/include/soro/utls/coroutine/recursive_generator.h +++ b/include/soro/utls/coroutine/recursive_generator.h @@ -9,12 +9,6 @@ #pragma once -#if defined(__clang__) -#include -#else -#include -#endif - #include #include #include @@ -26,12 +20,6 @@ namespace soro::utls { -#if defined(__clang__) -namespace coro = std::experimental; -#else -namespace coro = std; -#endif - template class [[nodiscard]] recursive_generator { public: diff --git a/include/soro/utls/ranges/to.h b/include/soro/utls/ranges/to.h new file mode 100644 index 00000000..466aa38d --- /dev/null +++ b/include/soro/utls/ranges/to.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace soro::utls { + +namespace detail { + +template +using range_value_t = decltype(*std::begin(std::declval())); + +template