diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 7b4c7a681..24e2c9c7d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,3 @@ -FROM ghcr.io/philips-software/amp-devcontainer-cpp:v5.1.4@sha256:46239906460dedb3baf3c33d9275f3de4f17d7a237fc136c2013b021589a6dbd +FROM ghcr.io/philips-software/amp-devcontainer-cpp:v5.2.0@sha256:c47fcc83b59fb08f3a3a6e591b18bad49b3862acc35770fca6cec9ad0adb9cb2 HEALTHCHECK NONE diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dbda8230..b9f2db25d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: name: Host Build & Test (ubuntu-latest) runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 @@ -30,7 +30,7 @@ jobs: key: ${{ github.job }}-ubuntu-latest variant: sccache - uses: seanmiddleditch/gha-setup-ninja@96bed6edff20d1dd61ecff9b75cc519d516e6401 # v5 - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host" buildPreset: "host-Debug-WithPackage" @@ -52,14 +52,14 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/philips-software/amp-devcontainer-cpp:5.1.4@sha256:46239906460dedb3baf3c33d9275f3de4f17d7a237fc136c2013b021589a6dbd # 5.1.4 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 with: key: ${{ github.job }}-ubuntu-latest - uses: seanmiddleditch/gha-setup-ninja@96bed6edff20d1dd61ecff9b75cc519d516e6401 # v5 - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host" buildPreset: "host-RelWithDebInfo" @@ -76,7 +76,7 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/philips-software/amp-devcontainer-cpp:5.1.4@sha256:46239906460dedb3baf3c33d9275f3de4f17d7a237fc136c2013b021589a6dbd # 5.1.4 steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -90,7 +90,7 @@ jobs: with: key: ${{ github.job }}-clang-msvc - uses: seanmiddleditch/gha-setup-ninja@96bed6edff20d1dd61ecff9b75cc519d516e6401 # v5 - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host-ClangMsvc" buildPreset: "host-ClangMsvc-Debug" @@ -102,14 +102,14 @@ jobs: matrix: os: [macos-latest, windows-latest, windows-2019] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 with: key: ${{ github.job }}-${{ matrix.os }} variant: sccache - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host-single-Debug" buildPreset: "host-single-Debug" @@ -125,7 +125,7 @@ jobs: name: Host Build without MbedTLS runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 @@ -133,7 +133,7 @@ jobs: key: ${{ github.job }}-ubuntu-latest variant: sccache - uses: seanmiddleditch/gha-setup-ninja@96bed6edff20d1dd61ecff9b75cc519d516e6401 # v5 - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host-no-mbedtls" buildPreset: "host-no-mbedtls-Debug" @@ -147,7 +147,7 @@ jobs: gcc: ["7-2018-q2", "8-2019-q3", "9-2020-q2", "10.3-2021.10"] configuration: ["RelWithDebInfo"] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - name: Install GNU Arm Embedded Toolchain ${{ matrix.gcc }} @@ -163,7 +163,7 @@ jobs: name: emil - run: tar -zxvf emil-*.tar.gz - run: mkdir install && mv emil-*/* install/ - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "embedded" buildPreset: "embedded-${{ matrix.configuration }}" @@ -172,7 +172,7 @@ jobs: name: Embedded Build Without Host Install runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - name: Install GNU Arm Embedded Toolchain 10.3-2021.10 @@ -183,7 +183,7 @@ jobs: - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 with: key: ${{ github.job }} - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "embedded" buildPreset: "embedded-RelWithDebInfo" @@ -196,7 +196,7 @@ jobs: matrix: rtos: ["FreeRTOS", "ThreadX"] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - name: Install GNU Arm Embedded Toolchain 10.3-2021.10 @@ -212,7 +212,7 @@ jobs: name: emil - run: tar -zxvf emil-*.tar.gz - run: mkdir install && mv emil-*/* install/ - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "embedded-${{ matrix.rtos }}" buildPreset: "embedded-${{ matrix.rtos }}-RelWithDebInfo" diff --git a/.github/workflows/dependency-scanner.yml b/.github/workflows/dependency-scanner.yml index 0c0543234..712c05fa8 100644 --- a/.github/workflows/dependency-scanner.yml +++ b/.github/workflows/dependency-scanner.yml @@ -16,7 +16,7 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - uses: philips-forks/cmake-dependency-submission@72880580a7cafc16145d82268f1892c0ece3da2a # main dependency-review: runs-on: ubuntu-latest @@ -25,7 +25,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4 with: comment-summary-in-pr: true diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 362e5c89f..d9674851c 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -16,7 +16,7 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 0 persist-credentials: false @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 0 persist-credentials: false @@ -69,7 +69,7 @@ jobs: name: Publish to GitHub Pages steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Retrieve Antora Site uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 with: diff --git a/.github/workflows/linting-formatting.yml b/.github/workflows/linting-formatting.yml index c74f94bcb..0a265e6d2 100644 --- a/.github/workflows/linting-formatting.yml +++ b/.github/workflows/linting-formatting.yml @@ -22,7 +22,7 @@ jobs: pull-requests: write security-events: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 0 persist-credentials: false @@ -32,7 +32,7 @@ jobs: VALIDATE_ALL_CODEBASE: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: git diff - - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 if: ${{ success() || failure() }} with: sarif_file: megalinter-reports/megalinter-report.sarif @@ -42,6 +42,6 @@ jobs: name: linter path: | megalinter-reports - - uses: reviewdog/action-suggester@63b8f8cc21dfa052ac44436e65ed31edcffcb6c1 # v1.17.0 + - uses: reviewdog/action-suggester@db4abb16fbaabe386831e5addb7be1485d0d63d3 # v1.18.0 with: tool_name: MegaLinter diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index eb2e612c8..f12968c0b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -20,7 +20,7 @@ jobs: releases_created: ${{ steps.release.outputs.releases_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: - - uses: actions/create-github-app-token@31c86eb3b33c9b601a1f60f98dcbfd1d70f379b4 # v1.10.3 + - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 id: token with: app-id: ${{ vars.FOREST_RELEASER_APP_ID }} @@ -40,14 +40,14 @@ jobs: matrix: os: [macos-latest, ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 with: key: ${{ github.job }}-${{ matrix.os }} variant: sccache - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host-single-MinSizeRel" buildPreset: "release-package" diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 063459d66..b189de75a 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -21,7 +21,7 @@ jobs: actions: read contents: read steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - name: Analysis @@ -31,6 +31,6 @@ jobs: results_format: sarif repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} publish_results: true - - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/upload-sarif@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 with: sarif_file: scorecards.sarif diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 28859f2de..4ee9d4817 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -23,7 +23,7 @@ jobs: SONAR_SCANNER_VERSION: 5.0.1.3006 SONAR_SERVER_URL: "https://sonarcloud.io" steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: 0 # Disable shallow clone to enable blame information persist-credentials: false @@ -42,7 +42,7 @@ jobs: cmake --build --preset coverage GTEST_OUTPUT="xml:${PWD}/testresults/" ctest --preset coverage gcovr --sonarqube=coverage.xml --exclude-lines-by-pattern '.*assert\(.*\);|.*really_assert\(.*\);|.*std::abort();' --exclude-unreachable-branches --exclude-throw-branches -j "$(nproc)" --exclude=.*/generated/.* --exclude=.*/examples/.* --exclude=.*/external/.* --exclude=.*/lwip/.* --exclude=.*/tracing/.* --exclude=.*/test/.* - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "mutation-testing" buildPreset: "mutation-testing" @@ -69,18 +69,18 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@ed74d11c0b343532753ecead8a951bb09bb34bc9 # v1.2.14 with: key: ${{ github.job }} - - uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 with: languages: cpp - - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + - uses: lukka/run-cmake@af1be47fd7c933593f687731bc6fdbee024d3ff4 # v10.8 with: configurePreset: "host" buildPreset: "host-Debug" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - - uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10 diff --git a/protobuf/echo/TracingEcho.cpp b/protobuf/echo/TracingEcho.cpp index 2f64d9ef4..1f56fbf98 100644 --- a/protobuf/echo/TracingEcho.cpp +++ b/protobuf/echo/TracingEcho.cpp @@ -47,7 +47,7 @@ namespace services { tracer.Continue() << "["; for (auto v : value) - tracer.Continue() << infra::hex << infra::Width(2, 0) << v; + tracer.Continue() << infra::hex << infra::Width(2, '0') << v; tracer.Continue() << "]"; } @@ -168,7 +168,7 @@ namespace services auto save = stream.Reader().ConstructSaveMarker(); auto [contents, methodId] = parser.GetPartialField(); - if (stream.Failed() || (contents.Is() && contents.Get().length <= writerBuffer.max_size() - writerBuffer.size())) + if (stream.Failed() || !contents.Is() || contents.Get().length > writerBuffer.max_size() - writerBuffer.size()) break; if (contents.Is() && contents.Get().length > stream.Available()) diff --git a/protobuf/echo/TracingEcho.hpp b/protobuf/echo/TracingEcho.hpp index 61a60cfba..ddf6622c7 100644 --- a/protobuf/echo/TracingEcho.hpp +++ b/protobuf/echo/TracingEcho.hpp @@ -37,22 +37,17 @@ namespace services PrintField(static_cast(value), tracer); } - template - void PrintSubFields(const T& value, services::Tracer& tracer, typename T::template Type* = 0) + template + void PrintSubFields(const T& value, services::Tracer& tracer, std::index_sequence) { - PrintField(value.Get(std::integral_constant()), tracer); - PrintSubFields(value, tracer); + (void)((PrintField(value.Get(std::integral_constant()), tracer), true) && ...); } - template - void PrintSubFields(const T& value, services::Tracer& tracer, ...) - {} - template void PrintField(const T& value, services::Tracer& tracer, typename T::template Type<0>* = 0) { tracer.Continue() << "{"; - PrintSubFields<0>(value, tracer); + PrintSubFields(value, tracer, std::make_index_sequence{}); tracer.Continue() << "}"; } diff --git a/services/ble/test/TestGapCentral.cpp b/services/ble/test/TestGapCentral.cpp index 4d3565f46..ec93a2e01 100644 --- a/services/ble/test/TestGapCentral.cpp +++ b/services/ble/test/TestGapCentral.cpp @@ -34,12 +34,14 @@ namespace services TEST_F(GapCentralDecoratorTest, forward_all_state_changed_events_to_observers) { EXPECT_CALL(gapObserver, StateChanged(GapState::connected)); + EXPECT_CALL(gapObserver, StateChanged(GapState::initiating)); EXPECT_CALL(gapObserver, StateChanged(GapState::scanning)); EXPECT_CALL(gapObserver, StateChanged(GapState::standby)); gap.NotifyObservers([](GapCentralObserver& obs) { obs.StateChanged(GapState::connected); + obs.StateChanged(GapState::initiating); obs.StateChanged(GapState::scanning); obs.StateChanged(GapState::standby); }); diff --git a/services/echo_console/Console.cpp b/services/echo_console/Console.cpp index b9f943d07..92fcba122 100644 --- a/services/echo_console/Console.cpp +++ b/services/echo_console/Console.cpp @@ -1,5 +1,5 @@ #include "services/echo_console/Console.hpp" -#include "infra/stream/ByteOutputStream.hpp" +#include "infra/stream/StdVectorOutputStream.hpp" #include "infra/stream/StringInputStream.hpp" #include "services/tracer/GlobalTracer.hpp" #include @@ -299,14 +299,34 @@ namespace application ++parseIndex; } - while (parseIndex != line.size() && std::isdigit(line[parseIndex])) - ++parseIndex; + int32_t value = 0; + if (parseIndex != line.size() && line.substr(parseIndex, 2) == "0x") + { + tokenStart += 2; + parseIndex += 2; + while (parseIndex != line.size() && std::isxdigit(line[parseIndex])) + ++parseIndex; - std::string integer = line.substr(tokenStart, parseIndex - tokenStart); + std::string integer = line.substr(tokenStart, parseIndex - tokenStart); - int32_t value = 0; - for (std::size_t index = sign ? 1 : 0; index < integer.size(); ++index) - value = value * 10 + integer[index] - '0'; + for (std::size_t index = sign ? 1 : 0; index < integer.size(); ++index) + { + if (std::isdigit(integer[index])) + value = value * 16 + integer[index] - '0'; + else + value = value * 16 + std::tolower(integer[index]) - 'a' + 10; + } + } + else + { + while (parseIndex != line.size() && std::isdigit(line[parseIndex])) + ++parseIndex; + + std::string integer = line.substr(tokenStart, parseIndex - tokenStart); + + for (std::size_t index = sign ? 1 : 0; index < integer.size(); ++index) + value = value * 10 + integer[index] - '0'; + } if (sign) value *= -1; @@ -330,17 +350,22 @@ namespace application return ConsoleToken::String(tokenStart, identifier); } - Console::Console(EchoRoot& root) + Console::Console(EchoRoot& root, bool stopOnNetworkClose) : root(root) - , eventDispatcherThread([this]() + , eventDispatcherThread([this, stopOnNetworkClose]() { - RunEventDispatcher(); + RunEventDispatcher(stopOnNetworkClose); }) {} void Console::Run() { - while (!quit) + { + std::unique_lock lock(mutex); + started = true; + } + + while (!quit && !stoppedEventDispatcher) { std::string line; std::getline(std::cin, line); @@ -364,7 +389,7 @@ namespace application condition.notify_all(); }); - while (!processDone) + while (!processDone && !stoppedEventDispatcher) condition.wait(lock); } } @@ -592,14 +617,21 @@ namespace application std::cout << "Received method call " << methodId << " for unknown service " << serviceId << std::endl; } - void Console::RunEventDispatcher() + void Console::RunEventDispatcher(bool stopOnNetworkClose) { try { - network.Run(); + network.ExecuteUntil([this, stopOnNetworkClose]() + { + std::unique_lock lock(mutex); + return !network.NetworkActivity() && stopOnNetworkClose && started; + }); } catch (Quit&) {} + + std::unique_lock lock(mutex); + stoppedEventDispatcher = true; } void Console::ListInterfaces() @@ -745,7 +777,7 @@ namespace application MethodInvocation methodInvocation(line); auto [service, method] = SearchMethod(methodInvocation); - infra::ByteOutputStream::WithStorage<4096> stream; + infra::StdVectorOutputStream::WithStorage stream; infra::ProtoFormatter formatter(stream); formatter.PutVarInt(service->serviceId); @@ -754,8 +786,7 @@ namespace application methodInvocation.EncodeParameters(method.parameter, line.size(), formatter); } - auto range = infra::ReinterpretCastMemoryRange(stream.Writer().Processed()); - GetObserver().Send(std::string(range.begin(), range.end())); + GetObserver().Send(infra::ByteRangeAsStdString(infra::MakeRange(stream.Storage()))); } catch (ConsoleExceptions::SyntaxError& error) { @@ -767,11 +798,11 @@ namespace application } catch (ConsoleExceptions::MissingParameter& error) { - services::GlobalTracer().Trace() << "Missing parameter at index " << error.index << " (contents after that position is " << line.substr(error.index) << ")\n"; + services::GlobalTracer().Trace() << "Missing parameter at index " << error.index << " of type " << error.missingType << " (contents after that position is " << line.substr(error.index) << ")\n"; } catch (ConsoleExceptions::IncorrectType& error) { - services::GlobalTracer().Trace() << "Incorrect type at index " << error.index << " (contents after that position is " << line.substr(error.index) << ")\n"; + services::GlobalTracer().Trace() << "Incorrect type at index " << error.index << " expected type " << error.correctType << " (contents after that position is " << line.substr(error.index) << ")\n"; } catch (ConsoleExceptions::MethodNotFound& error) { @@ -942,25 +973,22 @@ namespace application if (!currentToken.Is()) throw ConsoleExceptions::SyntaxError{ IndexOf(currentToken) }; - currentToken = tokenizer.Token(); return result; } - std::vector Console::MethodInvocation::ProcessArray() + Console::MessageTokens Console::MethodInvocation::ProcessArray() { - std::vector result; + Console::MessageTokens result; while (true) { - Console::MessageTokens message; while (!currentToken.Is() && !currentToken.Is() && !currentToken.Is()) { - message.tokens.push_back(CreateMessageTokenValue()); + result.tokens.push_back(CreateMessageTokenValue()); currentToken = tokenizer.Token(); } - result.push_back(message); if (!currentToken.Is()) break; @@ -969,7 +997,6 @@ namespace application if (!currentToken.Is()) throw ConsoleExceptions::SyntaxError{ IndexOf(currentToken) }; - currentToken = tokenizer.Token(); return result; } @@ -981,7 +1008,7 @@ namespace application for (auto field : message.fields) { if (tokens.empty()) - throw ConsoleExceptions::MissingParameter{ valueIndex }; + throw ConsoleExceptions::MissingParameter{ valueIndex, field->protoType }; EncodeField(*field, tokens.front().first, tokens.front().second, formatter); tokens.erase(tokens.begin()); } @@ -1002,7 +1029,7 @@ namespace application void VisitInt64(const EchoFieldInt64& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1010,7 +1037,7 @@ namespace application void VisitUint64(const EchoFieldUint64& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1018,7 +1045,7 @@ namespace application void VisitInt32(const EchoFieldInt32& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1026,7 +1053,7 @@ namespace application void VisitFixed32(const EchoFieldFixed32& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutFixed32Field(static_cast(value.Get()), field.number); } @@ -1034,7 +1061,7 @@ namespace application void VisitFixed64(const EchoFieldFixed64& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutFixed64Field(static_cast(value.Get()), field.number); } @@ -1042,7 +1069,7 @@ namespace application void VisitBool(const EchoFieldBool& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "bool" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1050,7 +1077,7 @@ namespace application void VisitString(const EchoFieldString& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "string" }; formatter.PutStringField(infra::BoundedConstString(value.Get().data(), value.Get().size()), field.number); } @@ -1058,7 +1085,7 @@ namespace application void VisitUnboundedString(const EchoFieldUnboundedString& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "string" }; formatter.PutStringField(infra::BoundedConstString(value.Get().data(), value.Get().size()), field.number); } @@ -1066,7 +1093,7 @@ namespace application void VisitEnum(const EchoFieldEnum& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1074,7 +1101,7 @@ namespace application void VisitSFixed32(const EchoFieldSFixed32& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutFixed32Field(static_cast(value.Get()), field.number); } @@ -1082,7 +1109,7 @@ namespace application void VisitSFixed64(const EchoFieldSFixed64& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutFixed64Field(static_cast(value.Get()), field.number); } @@ -1090,55 +1117,28 @@ namespace application void VisitMessage(const EchoFieldMessage& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, field.protoType }; - methodInvocation.EncodeMessage(*field.message, value.Get(), valueIndex, formatter); + infra::StdVectorOutputStream::WithStorage stream; + infra::ProtoFormatter messageFormatter(stream); + methodInvocation.EncodeMessage(*field.message, value.Get(), valueIndex, messageFormatter); + formatter.PutLengthDelimitedField(infra::MakeRange(stream.Storage()), field.number); } void VisitBytes(const EchoFieldBytes& field) override { - if (!value.Is>()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; - std::vector bytes; - for (auto& messageTokens : value.Get>()) - { - if (messageTokens.tokens.size() < 1) - throw ConsoleExceptions::MissingParameter{ valueIndex }; - if (messageTokens.tokens.size() > 1) - throw ConsoleExceptions::TooManyParameters{ messageTokens.tokens[1].second }; - if (!messageTokens.tokens.front().first.Is()) - throw ConsoleExceptions::IncorrectType{ messageTokens.tokens[0].second }; - - bytes.push_back(static_cast(messageTokens.tokens.front().first.Get())); - } - - formatter.PutBytesField(infra::MakeRange(bytes), field.number); + PutVector(field.number); } void VisitUnboundedBytes(const EchoFieldUnboundedBytes& field) override { - if (!value.Is>()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; - std::vector bytes; - for (auto& messageTokens : value.Get>()) - { - if (messageTokens.tokens.size() < 1) - throw ConsoleExceptions::MissingParameter{ valueIndex }; - if (messageTokens.tokens.size() > 1) - throw ConsoleExceptions::TooManyParameters{ messageTokens.tokens[1].second }; - if (!messageTokens.tokens.front().first.Is()) - throw ConsoleExceptions::IncorrectType{ messageTokens.tokens[0].second }; - - bytes.push_back(static_cast(messageTokens.tokens.front().first.Get())); - } - - formatter.PutBytesField(infra::MakeRange(bytes), field.number); + PutVector(field.number); } void VisitUint32(const EchoFieldUint32& field) override { if (!value.Is()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, "integer" }; formatter.PutVarIntField(value.Get(), field.number); } @@ -1154,39 +1154,40 @@ namespace application void VisitRepeated(const EchoFieldRepeated& field) override { - if (!value.Is>()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + PutRepeated(field.protoType, field.type); + } - for (auto& messageTokens : value.Get>()) + void VisitUnboundedRepeated(const EchoFieldUnboundedRepeated& field) override + { + PutRepeated(field.protoType, field.type); + } + + private: + void PutVector(int fieldNumber) + { + if (!value.Is()) + throw ConsoleExceptions::IncorrectType{ valueIndex, "vector of integers" }; + std::vector bytes; + for (auto& messageToken : value.Get().tokens) { - if (messageTokens.tokens.size() < 1) - throw ConsoleExceptions::MissingParameter{ valueIndex }; - if (messageTokens.tokens.size() > 1) - throw ConsoleExceptions::TooManyParameters{ messageTokens.tokens[1].second }; - if (!messageTokens.tokens.front().first.Is()) - throw ConsoleExceptions::IncorrectType{ messageTokens.tokens.front().second }; + if (!messageToken.first.Is()) + throw ConsoleExceptions::IncorrectType{ messageToken.second, "integer" }; - EncodeFieldVisitor visitor(messageTokens, valueIndex, formatter, methodInvocation); - field.type->Accept(visitor); + bytes.push_back(static_cast(messageToken.first.Get())); } + + formatter.PutBytesField(infra::MakeRange(bytes), fieldNumber); } - void VisitUnboundedRepeated(const EchoFieldUnboundedRepeated& field) override + void PutRepeated(const std::string& fieldProtoType, std::shared_ptr fieldType) { if (!value.Is>()) - throw ConsoleExceptions::IncorrectType{ valueIndex }; + throw ConsoleExceptions::IncorrectType{ valueIndex, fieldProtoType }; for (auto& messageTokens : value.Get>()) { - if (messageTokens.tokens.size() < 1) - throw ConsoleExceptions::MissingParameter{ valueIndex }; - if (messageTokens.tokens.size() > 1) - throw ConsoleExceptions::TooManyParameters{ messageTokens.tokens[1].second }; - if (!messageTokens.tokens.front().first.Is()) - throw ConsoleExceptions::IncorrectType{ messageTokens.tokens.front().second }; - EncodeFieldVisitor visitor(messageTokens, valueIndex, formatter, methodInvocation); - field.type->Accept(visitor); + fieldType->Accept(visitor); } } diff --git a/services/echo_console/Console.hpp b/services/echo_console/Console.hpp index c91e276b8..9a6425deb 100644 --- a/services/echo_console/Console.hpp +++ b/services/echo_console/Console.hpp @@ -186,7 +186,7 @@ namespace application : public infra::Subject { public: - explicit Console(EchoRoot& root); + explicit Console(EchoRoot& root, bool stopOnNetworkClose); void Run(); services::ConnectionFactory& ConnectionFactory(); @@ -219,7 +219,7 @@ namespace application void ProcessParameterTokens(); std::pair CreateMessageTokenValue(); MessageTokens::MessageTokenValue ProcessMessage(); - std::vector ProcessArray(); + Console::MessageTokens ProcessArray(); void EncodeMessage(const EchoMessage& message, const MessageTokens& messageTokens, std::size_t valueIndex, infra::ProtoFormatter& formatter); void EncodeField(const EchoField& field, const MessageTokens::MessageTokenValue& value, std::size_t valueIndex, infra::ProtoFormatter& formatter); @@ -235,7 +235,7 @@ namespace application void PrintField(infra::Variant& fieldData, const EchoField& field, infra::ProtoParser& parser); void MethodNotFound(const EchoService& service, uint32_t methodId) const; void ServiceNotFound(uint32_t serviceId, uint32_t methodId) const; - void RunEventDispatcher(); + void RunEventDispatcher(bool stopOnNetworkClose); void ListInterfaces(); void ListFields(const EchoMessage& message); void Process(const std::string& line) const; @@ -246,7 +246,9 @@ namespace application main_::NetworkAdapter network; hal::TimerServiceGeneric timerService{ infra::systemTimerServiceId }; std::thread eventDispatcherThread; + bool started = false; bool quit = false; + bool stoppedEventDispatcher = false; std::mutex mutex; std::condition_variable condition; bool processDone = false; @@ -273,11 +275,13 @@ namespace application struct MissingParameter { std::size_t index; + std::string missingType; }; struct IncorrectType { std::size_t index; + std::string correctType; }; } } diff --git a/services/echo_console/Main.cpp b/services/echo_console/Main.cpp index 883346f4e..1a61b8178 100644 --- a/services/echo_console/Main.cpp +++ b/services/echo_console/Main.cpp @@ -185,7 +185,8 @@ ConsoleClientTcp::ConsoleClientTcp(services::ConnectionFactoryWithNameResolver& ConsoleClientTcp::~ConsoleClientTcp() { - consoleClientConnection->services::ConnectionObserver::Subject().AbortAndDestroy(); + if (!!consoleClientConnection) + consoleClientConnection->services::ConnectionObserver::Subject().AbortAndDestroy(); } infra::BoundedConstString ConsoleClientTcp::Hostname() const @@ -317,7 +318,8 @@ int main(int argc, char* argv[], const char* env[]) std::cout << "Loaded " << path << std::endl; } - application::Console console(root); + bool serialConnectionRequested = get(target).substr(0, 3) == "COM" || get(target).substr(0, 4) == "/dev"; + application::Console console(root, !serialConnectionRequested); services::ConnectionFactoryWithNameResolverImpl::WithStorage<4> connectionFactory(console.ConnectionFactory(), console.NameResolver()); infra::Optional consoleClientTcp; infra::Optional consoleClientWebSocket; @@ -325,24 +327,17 @@ int main(int argc, char* argv[], const char* env[]) infra::Optional> bufferedUart; infra::Optional consoleClientUart; - auto construct = [&]() + if (serialConnectionRequested) { - if (get(target).substr(0, 3) == "COM" || get(target).substr(0, 4) == "/dev") - { - uart.Emplace(get(target)); - bufferedUart.Emplace(*uart); - consoleClientUart.Emplace(console, *bufferedUart); - } - else if (services::SchemeFromUrl(infra::BoundedConstString(get(target))) == "ws") - consoleClientWebSocket.Emplace(connectionFactory, console, get(target), randomDataGenerator, tracer); - else - consoleClientTcp.Emplace(connectionFactory, console, get(target), tracer); - }; + uart.Emplace(get(target)); + bufferedUart.Emplace(*uart); + consoleClientUart.Emplace(console, *bufferedUart); + } + else if (services::SchemeFromUrl(infra::BoundedConstString(get(target))) == "ws") + consoleClientWebSocket.Emplace(connectionFactory, console, get(target), randomDataGenerator, tracer); + else + consoleClientTcp.Emplace(connectionFactory, console, get(target), tracer); - infra::EventDispatcher::Instance().Schedule([&construct]() - { - construct(); - }); console.Run(); } catch (const args::Help&)