From 4c52ed2753ca8194681b87d786d887f2a3b4d2ad Mon Sep 17 00:00:00 2001 From: "Penn, John M 047828115" Date: Wed, 18 Aug 2021 13:15:10 -0500 Subject: [PATCH] Merge Caleb Herpins port of Trick webserver from Mongoose to CivetWeb #730. --- .github/workflows/python_tests_32.yml | 80 ++ .github/workflows/python_tests_linux.yml | 186 +++++ .github/workflows/python_tests_macos.yml | 53 ++ .github/workflows/test_linux.yml | 2 +- .gitignore | 10 +- Makefile | 64 +- autoconf/configure.ac | 37 +- configure | 71 +- ...Adding_SSL_encryption_to_Your_Webserver.md | 36 + .../web/Adding_a_Web_Server_to_Your_Sim.html | 558 ------------- .../web/Adding_a_Web_Server_to_Your_Sim.md | 71 +- .../web/Extending_the_HTTP-API.html | 583 -------------- .../web/Extending_the_HTTP-API.md | 128 +-- .../web/Extending_the_WS-API.html | 736 ------------------ .../documentation/web/Extending_the_WS-API.md | 31 +- .../web/HTTP-API_alloc_info.html | 633 --------------- .../web/WS-API_VariableServer.html | 659 ---------------- docs/documentation/web/index.html | 44 -- include/trick/MyCivetServer.hh | 85 ++ include/trick/WebServer.hh | 69 -- include/trick/WebSocketSession.hh | 6 +- include/trick/files_to_ICG.hh | 4 +- share/trick/makefiles/Makefile.common | 12 +- share/trick/makefiles/config_user.mk.in | 2 +- share/trick/pymods/trick/.gitignore | 6 + share/trick/pymods/trick/conftest.py | 68 ++ share/trick/pymods/trick/makefile | 12 + share/trick/pymods/trick/pytest.ini | 3 + share/trick/pymods/trick/requirements.txt | 5 + share/trick/pymods/trick/run_tests.py | 8 + .../trick/tests/civet_server/test_http.py | 107 +++ .../trick/tests/civet_server/test_misc.py | 9 + .../trick/tests/civet_server/test_ws.py | 100 +++ share/trick/pymods/trick/utils.py | 100 +++ share/trick/sim_objects/CivetServer.sm | 20 + share/trick/sim_objects/WebServer.sm | 20 - trick_sims/Cannon/SIM_cannon_numeric/S_define | 2 +- .../Modified_data/cannon.dr | 17 + .../Modified_data/realtime.py | 11 + .../Cannon/SIM_cannon_webserver/README.md | 31 + .../RUN_graphics/input.py | 27 + .../SIM_cannon_webserver/RUN_test/input.py | 11 + .../RUN_test/unit_test.py | 3 + .../Cannon/SIM_cannon_webserver/S_define | 40 + .../SIM_cannon_webserver/S_overrides.mk | 3 + .../images/CannonInit.png | Bin 0 -> 46380 bytes .../www/apps/alloc_info.html | 156 ++++ .../www/apps/react/TrickLogoSmall.png | Bin 0 -> 28273 bytes .../www/apps/react/asset-manifest.json | 18 + .../www/apps/react/index.html | 1 + .../www/apps/react/manifest.json | 15 + ...nifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js | 122 +++ .../www/apps/react/service-worker.js | 39 + .../react/static/css/2.335e4b44.chunk.css | 2 + .../react/static/css/2.335e4b44.chunk.css.map | 1 + .../react/static/css/main.a82b4685.chunk.css | 2 + .../static/css/main.a82b4685.chunk.css.map | 1 + .../apps/react/static/js/2.fd0d41cf.chunk.js | 2 + .../react/static/js/2.fd0d41cf.chunk.js.map | 1 + .../react/static/js/main.593ee672.chunk.js | 2 + .../static/js/main.593ee672.chunk.js.map | 1 + .../react/static/js/runtime~main.d653cc00.js | 2 + .../static/js/runtime~main.d653cc00.js.map | 1 + .../SIM_cannon_webserver/www/apps/time.html | 36 + .../www/apps/vs_connections.html | 74 ++ .../SIM_cannon_webserver/www/apps/wsexp.html | 98 +++ .../www/images/trick_icon.png | Bin 0 -> 11563 bytes .../SIM_cannon_webserver/www/index.html | 35 + .../Cannon/SIM_cannon_webserver/www/style.css | 19 + .../httpMethods/handle_HTTP_GET_hello.c | 9 + .../httpMethods/handle_HTTP_GET_hello.h | 8 + .../Cannon/models/httpMethods/TimeSession.cpp | 55 ++ .../Cannon/models/httpMethods/TimeSession.hh | 25 + .../httpMethods/handle_HTTP_GET_hello.c | 11 + .../httpMethods/handle_HTTP_GET_hello.h | 8 + trick_source/trick_swig/Makefile | 4 +- trick_source/trick_swig/sim_services.i | 2 +- .../include/VariableServerSession.hh | 6 +- .../include/VariableServerVariable.hh | 6 +- .../CivetServer/include/http_GET_handlers.hh | 48 ++ .../include/simpleJSON.hh | 0 .../web/{HttpServer => CivetServer}/makefile | 6 +- .../web/CivetServer/src/MyCivetServer.cpp | 394 ++++++++++ .../src/VariableServerSession.cpp | 10 +- .../src/VariableServerVariable.cpp | 2 + .../web/CivetServer/src/http_GET_handlers.cpp | 194 +++++ .../src/simpleJSON.cpp | 0 .../HttpServer/include/http_GET_handlers.hh | 14 - trick_source/web/HttpServer/src/WebServer.cpp | 425 ---------- .../web/HttpServer/src/http_GET_handlers.cpp | 65 -- 90 files changed, 2663 insertions(+), 4020 deletions(-) create mode 100644 .github/workflows/python_tests_32.yml create mode 100644 .github/workflows/python_tests_linux.yml create mode 100644 .github/workflows/python_tests_macos.yml create mode 100644 docs/documentation/web/Adding_SSL_encryption_to_Your_Webserver.md delete mode 100644 docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.html delete mode 100644 docs/documentation/web/Extending_the_HTTP-API.html delete mode 100644 docs/documentation/web/Extending_the_WS-API.html delete mode 100644 docs/documentation/web/HTTP-API_alloc_info.html delete mode 100644 docs/documentation/web/WS-API_VariableServer.html delete mode 100644 docs/documentation/web/index.html create mode 100644 include/trick/MyCivetServer.hh delete mode 100644 include/trick/WebServer.hh create mode 100644 share/trick/pymods/trick/.gitignore create mode 100644 share/trick/pymods/trick/conftest.py create mode 100644 share/trick/pymods/trick/makefile create mode 100644 share/trick/pymods/trick/pytest.ini create mode 100644 share/trick/pymods/trick/requirements.txt create mode 100755 share/trick/pymods/trick/run_tests.py create mode 100644 share/trick/pymods/trick/tests/civet_server/test_http.py create mode 100644 share/trick/pymods/trick/tests/civet_server/test_misc.py create mode 100644 share/trick/pymods/trick/tests/civet_server/test_ws.py create mode 100644 share/trick/pymods/trick/utils.py create mode 100644 share/trick/sim_objects/CivetServer.sm delete mode 100644 share/trick/sim_objects/WebServer.sm create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/Modified_data/cannon.dr create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/Modified_data/realtime.py create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/README.md create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/RUN_test/unit_test.py create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/S_define create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/images/CannonInit.png create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/TrickLogoSmall.png create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/asset-manifest.json create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/index.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/manifest.json create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/service-worker.js create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css.map create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css.map create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/2.fd0d41cf.chunk.js create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/2.fd0d41cf.chunk.js.map create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/main.593ee672.chunk.js create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/main.593ee672.chunk.js.map create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/runtime~main.d653cc00.js create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/runtime~main.d653cc00.js.map create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/time.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/vs_connections.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/apps/wsexp.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/images/trick_icon.png create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/index.html create mode 100644 trick_sims/Cannon/SIM_cannon_webserver/www/style.css create mode 100644 trick_sims/Cannon/models/cannon/httpMethods/handle_HTTP_GET_hello.c create mode 100644 trick_sims/Cannon/models/cannon/httpMethods/handle_HTTP_GET_hello.h create mode 100644 trick_sims/Cannon/models/httpMethods/TimeSession.cpp create mode 100644 trick_sims/Cannon/models/httpMethods/TimeSession.hh create mode 100644 trick_sims/Cannon/models/httpMethods/handle_HTTP_GET_hello.c create mode 100644 trick_sims/Cannon/models/httpMethods/handle_HTTP_GET_hello.h rename trick_source/web/{HttpServer => CivetServer}/include/VariableServerSession.hh (96%) rename trick_source/web/{HttpServer => CivetServer}/include/VariableServerVariable.hh (94%) create mode 100644 trick_source/web/CivetServer/include/http_GET_handlers.hh rename trick_source/web/{HttpServer => CivetServer}/include/simpleJSON.hh (100%) rename trick_source/web/{HttpServer => CivetServer}/makefile (89%) create mode 100644 trick_source/web/CivetServer/src/MyCivetServer.cpp rename trick_source/web/{HttpServer => CivetServer}/src/VariableServerSession.cpp (95%) rename trick_source/web/{HttpServer => CivetServer}/src/VariableServerVariable.cpp (99%) create mode 100644 trick_source/web/CivetServer/src/http_GET_handlers.cpp rename trick_source/web/{HttpServer => CivetServer}/src/simpleJSON.cpp (100%) delete mode 100644 trick_source/web/HttpServer/include/http_GET_handlers.hh delete mode 100644 trick_source/web/HttpServer/src/WebServer.cpp delete mode 100644 trick_source/web/HttpServer/src/http_GET_handlers.cpp diff --git a/.github/workflows/python_tests_32.yml b/.github/workflows/python_tests_32.yml new file mode 100644 index 00000000..67534285 --- /dev/null +++ b/.github/workflows/python_tests_32.yml @@ -0,0 +1,80 @@ +name: Python Tests 32-bit + +on: + push: + paths-ignore: + - 'docs/**' + - '.github/workflows/**' + - '!.github/workflows/python_tests_32.yml' + pull_request: + +jobs: + trick_32bit: + runs-on: ubuntu-18.04 + container: docker://centos:7 + steps: + - name: Checkout repository + uses: actions/checkout@master + - name: Add yum repo + run: yum -y install epel-release.noarch && yum -y update + - name: Install python dependencies + run: yum install -y python3-venv python3 python3-pip python3-devel gcc + - name: Info + run: | + python3 --version + - name: Build Python environment + run: | + cd share/trick/pymods/trick + python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt + - name: Install dependency group + run: yum -y groupinstall "Development tools" && yum -y update + - name: Install other dependencies + run: > + yum remove -y swig && yum install -y llvm llvm-devel llvm-static clang clang-devel + bison flex gcc gcc-c++ libxml2-devel make cmake wget + ncurses-devel openmotif openmotif-devel python-devel perl + perl-Digest-MD5 swig3 zlib-devel glibc.x86_64 libxml2-devel.i686 + ncurses-devel.i686 zlib-devel.i686 python-libs.i686 + expat-2.1.0-10.el7_3.i686 glibc-devel-2.17-196.el7.i686 + glibc.i686 glibc-devel.i686 udunits2 udunits2-devel gtest-devel.i686 + java-11-openjdk java-11-openjdk-devel expat-devel.i686 + which gcc-gfortran git wget gsl-devel gtest-devel gsl-devel.i686 + maven udunits2 udunits2-devel zip python3-tkinter xorg-x11-server-Xvfb + - name: Symlink python + run: | + cd /usr/lib + ln -s ./libpython2.7.so.1.0 libpython2.7.so + - name: Install Udunits (32 bit) + run: | + cd / + curl --retry 4 -O https://artifacts.unidata.ucar.edu/repository/downloads-udunits/udunits-2.2.28.tar.gz + tar xfvz udunits-2.2.28.tar.gz + rm -rf udunits-2.2.28.tar.gz + cd udunits-2.2.28 + export CFLAGS="-m32" + ./configure + make + make install + - name: Install GTest (32 bit) + run: | + wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz + tar xfvz release-1.8.0.tar.gz + cd googletest-release-1.8.0/googletest + export CFLAGS="-m32" + export CXXFLAGS="-m32" + cmake . + make + make install + - name: Build Trick + run: | + yum -y update && yum clean all + export MAKEFLAGS=-j`nproc` + ./configure --without-hdf5 --enable-32bit + make + - name: Run civet tests + run: | + cd share/trick/pymods/trick/ + . .venv/bin/activate + ./run_tests.py + env: + TRICK_HOME: "${{ github.workspace }}" diff --git a/.github/workflows/python_tests_linux.yml b/.github/workflows/python_tests_linux.yml new file mode 100644 index 00000000..053f319f --- /dev/null +++ b/.github/workflows/python_tests_linux.yml @@ -0,0 +1,186 @@ +name: Python Tests Linux + +on: + push: + paths-ignore: + - 'docs/**' + - '.github/workflows/**' + - '!.github/workflows/python_tests_linux.yml' + pull_request: + +jobs: + build: + strategy: + fail-fast: false + matrix: + cfg: +#-------- Operating Systems ---------------- + - { os: ubuntu, tag: 18.04, arch: debian } # EOL April 2023 + - { os: ubuntu, tag: 20.04, arch: debian } # EOL April 2025 + - { os: debian, tag: 10, arch: debian } # EOL 2024 + - { os: centos, tag: 7, arch: rhel } # EOL June 2024 + - { os: centos, tag: latest, arch: rhel } # 8 as of April 2020 + # - { os: fedora, tag: latest, arch: rhel } # 31 as of April 2020 + # - { os: fedora, tag: 33, arch: rhel } # feeling confident? + # - { os: fedora, tag: rawhide, arch: rhel } # for thrill-seekers only + +#-------- Defaults -------------------------- + include: + - cfg: {} + deps: >- + bison + clang + flex + git + llvm + make + maven + cmake + zip + install_gtest: echo gtest already installed + conf_pkg: echo package manager already configured + install_cmd: install -y +#-------- Debian-based Dependencies ---------------- + - cfg: { arch: debian } + pkg_mgr: apt-get + conf_pkg: DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y tzdata + arch_deps: >- + swig + curl + g++ + libx11-dev + libxml2-dev + libxt-dev + libmotif-common + libmotif-dev + python2.7-dev + zlib1g-dev + llvm-dev + libclang-dev + libudunits2-dev + libgtest-dev + python3 + python3-tk + python3-venv + python3-dev + xvfb + install_gtest: cd /usr/src/gtest && cmake . && make && cp libgtest* /usr/lib/ +#-------- RHEL Dependencies ---------------- + - cfg: { arch: rhel } + arch_deps: >- + clang-devel + gcc + gcc-c++ + java-11-openjdk-devel + libxml2-devel + llvm-devel + llvm-static + ncurses-devel + openmotif + openmotif-devel + perl + perl-Digest-MD5 + udunits2 + udunits2-devel + which + zlib-devel + gtest-devel + python3-devel + python3-tkinter + xorg-x11-server-Xvfb +#-------- Ubuntu Only Dependencies ---------------- + - cfg: { os: ubuntu } + os_deps: >- + openjdk-11-jdk +#-------- Debian OS Only Dependencies ---------------- + - cfg: { os: debian } + os_deps: >- + openjdk-11-jdk +#-------- CentOS Only Dependencies ---------------- + - cfg: { os: centos } + pkg_mgr: yum + conf_pkg: yum -y install epel-release && yum -y update + os_deps: >- + libX11-devel + libXt-devel +#-------- Fedora Only Dependencies ---------------- +# - cfg: { os: fedora } +# pkg_mgr: dnf +# os_deps: >- +# swig +# perl-Text-Balanced +# python-devel +# diffutils +#-------- Version Specific Dependencies ---------------- + - cfg: { os: ubuntu, tag: 20.04 } + conf_pkg: DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y tzdata + install_gtest: cd /usr/src/gtest && cmake . && make && cp lib/libgtest* /usr/lib/ + tag_deps: >- + python3.8-dev + - cfg: { os: centos, tag: 7 } + tag_deps: >- + swig3 + python-devel + - cfg: { os: centos, tag: latest } + pkg_mgr: dnf + conf_pkg: > + dnf -y install epel-release && + dnf -y update && + dnf install -y 'dnf-command(config-manager)' && + dnf config-manager --enable powertools + tag_deps: >- + swig + python3-devel diffutils +#-------- Job definition ---------------- + runs-on: ubuntu-18.04 + container: docker://${{matrix.cfg.os}}:${{matrix.cfg.tag}} + steps: + # - name: Set up Python ${{ matrix.python-version }} + # uses: actions/setup-python@v2 + # with: + # python-version: ${{ matrix.python-version }} + - name: Info + run: | + pwd + echo $TEST + echo workspace is $TRICK_HOME + echo files in workspace: + ls -la $TRICK_HOME + env: + TEST: Hello World! + TRICK_HOME: ${{ github.workspace }} + - name: Update Package Manager + run: ${{matrix.conf_pkg}} + - name: Install Dependencies + run: | + ${{matrix.pkg_mgr}} ${{matrix.install_cmd}} ${{matrix.deps}} ${{matrix.arch_deps}} ${{matrix.os_deps}} ${{matrix.tag_deps}} + - name: Install GTest + run: ${{matrix.install_gtest}} + - name: Checkout repository + uses: actions/checkout@master + - name: Info after checkout + run: | + pwd + echo $TEST + echo workspace is $TRICK_HOME + echo files in workspace: + ls -la $TRICK_HOME + env: + TEST: Hello World! + TRICK_HOME: ${{ github.workspace }} + - name: Create testing environment + run: | + cd share/trick/pymods/trick/ + python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt + - name: Build trick + run: | + export MAKEFLAGS=-j`nproc` + ./configure + make + - name: Run Civet Tests + run: | + cd share/trick/pymods/trick/ + . .venv/bin/activate + ./run_tests.py + env: + TRICK_HOME: "${{ github.workspace }}" \ No newline at end of file diff --git a/.github/workflows/python_tests_macos.yml b/.github/workflows/python_tests_macos.yml new file mode 100644 index 00000000..1bb2fbab --- /dev/null +++ b/.github/workflows/python_tests_macos.yml @@ -0,0 +1,53 @@ +name: Python Tests MacOS + +on: + push: + paths-ignore: + - 'docs/**' + - '.github/workflows/**' + - '!.github/workflows/python_tests_macos.yml' + pull_request: + +jobs: + macOS: + runs-on: macos-latest + steps: + - name: Checkout repository + uses: actions/checkout@master + - name: Install python + run: | + brew install python + python3 -m ensurepip --upgrade + pip3 install virtualenv + python3 --version + pip3 --version + - name: Install gtest + run: | + wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz + tar xzvf release-1.8.0.tar.gz + cd googletest-release-1.8.0/googletest + cmake . + make + make install + - name: Install dependencies + run: | + # sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.15.pkg -target / + brew install --cask xquartz + brew install llvm@11 swig udunits openmotif maven python-tk + brew link llvm llvm@11 + - name: Create testing environment + run: | + cd share/trick/pymods/trick/ + python3 -m virtualenv .venv && . .venv/bin/activate && pip install -r requirements.txt + - name: Build Trick + run: | + export MAKEFLAGS=-j4 + ./configure + make + - name: Run Civet Tests + run: | + cd share/trick/pymods/trick/ + . .venv/bin/activate + ./run_tests.py + env: + TRICK_HOME: "${{ github.workspace }}" \ No newline at end of file diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index bf89de2f..28491c5c 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -142,4 +142,4 @@ jobs: ./configure make - name: Test - run: make test + run: make test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0735fad6..b7d0bea1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,9 +27,11 @@ autom4te.cache trick_test gmon.out *init_log.csv* -include/mongoose/ -trick_source/web/HttpServer/mongoose.c -trick_source/web/HttpServer/mongoose.h -trick_source/web/HttpServer/obj/ +trick_source/web/CivetServer/obj/ trick-offline *sim_services_classes.resource +civetweb_clone/ +include/civet/ +.vscode/ +civet_server_error.log +server.pem \ No newline at end of file diff --git a/Makefile b/Makefile index be8bd0cd..3bb724bb 100644 --- a/Makefile +++ b/Makefile @@ -149,11 +149,10 @@ ifeq ($(USE_JAVA), 1) all: java endif -ifeq ($(TRICK_MONGOOSE), 1) -all: webserver -icg_sim_serv: ${TRICK_HOME}/include/mongoose/mongoose.h -ICG: ${TRICK_HOME}/include/mongoose/mongoose.h +ifeq ($(TRICK_CIVET), 1) +icg_sim_serv: ${TRICK_LIB_DIR}/libtrickCivet.a endif + #------------------------------------------------------------------------------- # 1.1 Build Trick-core no_dp: $(TRICK_LIB) $(TRICK_SWIG_LIB) @@ -219,47 +218,42 @@ dp: ${TRICK_HOME}/trick_source/trick_utils/units #------------------------------------------------------------------------------- # -.PHONY: webserver -webserver: ${TRICK_LIB_DIR}/libmongoose.a ${TRICK_HOME}/include/mongoose/mongoose.h - $(MAKE) -C ${TRICK_HOME}/trick_source/web/HttpServer #------------------------------------------------------------------------------- +# 1.2 Build Trick's CivetWeb webserver. -mongoose.h: - curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.h +CIVET_CLONE_DIR = civetweb_clone -mongoose.c: - curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.c +.PHONY: civetweb +civetweb: ${TRICK_LIB_DIR}/libtrickCivet.a -${TRICK_LIB_DIR}/libmongoose.a: ${TRICK_HOME}/include/mongoose/mongoose.h | mongoose.o $(TRICK_LIB_DIR) - ar crs $@ mongoose.o - @ rm mongoose.o - @ rm -f mongoose.h - @ echo ; echo "Mongoose library compiled:" ; date +${TRICK_LIB_DIR}/libtrickCivet.a: ${TRICK_LIB_DIR}/libcivetweb.a ${TRICK_HOME}/include/civet/civetweb.h ${TRICK_HOME}/include/civet/CivetServer.h + $(MAKE) -C ${TRICK_HOME}/trick_source/web/CivetServer -ifeq (${TRICK_OFFLINE}, 0) +${TRICK_LIB_DIR}/libcivetweb.a: ${CIVET_CLONE_DIR}/libcivetweb.a | ${TRICK_LIB_DIR} + cp ${CIVET_CLONE_DIR}/libcivetweb.a $(TRICK_LIB_DIR)/libcivetweb.a -mongoose.o: mongoose.h mongoose.c - $(CC) $(TRICK_CFLAGS) ${TRICK_SYSTEM_CXXFLAGS} -c -o mongoose.o mongoose.c - @ rm mongoose.c +${TRICK_HOME}/include/civet: + mkdir -p ${TRICK_HOME}/include/civet -${TRICK_HOME}/include/mongoose/mongoose.h: mongoose.h | ${TRICK_HOME}/include/mongoose - @ cp mongoose.h $@ +${TRICK_HOME}/include/civet/civetweb.h: ${CIVET_CLONE_DIR} ${TRICK_HOME}/include/civet + cp ${CIVET_CLONE_DIR}/include/civetweb.h ${TRICK_HOME}/include/civet/civetweb.h +${TRICK_HOME}/include/civet/CivetServer.h: ${CIVET_CLONE_DIR} ${TRICK_HOME}/include/civet + cp ${CIVET_CLONE_DIR}/include/CivetServer.h ${TRICK_HOME}/include/civet/CivetServer.h + + +ifeq (${TRICK_FORCE_32BIT},1) +CIVET_COMPILE_FAGS=-m32 else - -# if trick-offline gets updated, we should rebuild libmongoose -${TRICK_LIB_DIR}/libmongoose.a: ${TRICK_HOME}/trick-offline/mongoose.h ${TRICK_HOME}/trick-offline/mongoose.c - -mongoose.o: ${TRICK_HOME}/trick-offline/mongoose.h ${TRICK_HOME}/trick-offline/mongoose.c - $(CC) $(TRICK_CFLAGS) -c -I${TRICK_HOME}/trick-offline -o mongoose.o ${TRICK_HOME}/trick-offline/mongoose.c - -${TRICK_HOME}/include/mongoose/mongoose.h: ${TRICK_HOME}/trick-offline/mongoose.h | ${TRICK_HOME}/include/mongoose - @ cp ${TRICK_HOME}/trick-offline/mongoose.h $@ +CIVET_COMPILE_FAGS= endif -${TRICK_HOME}/include/mongoose: - @ mkdir $@ +${CIVET_CLONE_DIR}/libcivetweb.a: ${CIVET_CLONE_DIR} + $(MAKE) -C ${CIVET_CLONE_DIR} lib COPT=${CIVET_COMPILE_FAGS} WITH_CPP=1 WITH_WEBSOCKET=1 + +${CIVET_CLONE_DIR}: + git clone --branch v1.14 --depth 1 https://github.com/civetweb/civetweb.git $@ #------------------------------------------------------------------------------- # 1.3 Build Trick's Java Tools @@ -338,6 +332,10 @@ sim_test: @ $(MAKE) -C test @ $(MAKE) -C trick_sims test +pytest: + make -C share/trick/pymods/trick + + #requirements: # @ $(MAKE) -C trick_test/requirements_docs install diff --git a/autoconf/configure.ac b/autoconf/configure.ac index b5a5a262..00cd22ad 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -345,22 +345,6 @@ AC_ARG_ENABLE([offline], ) AC_SUBST([TRICK_OFFLINE]) -# If offline is specified, set some compilation flags -AC_ARG_ENABLE([mongoose], - AS_HELP_STRING([--enable-mongoose], [Compile Trick with webserver capabilites.]), - AS_IF([test "x$enable_mongoose" = xyes], - [ - TRICK_MONGOOSE="1" - AC_MSG_WARN($(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \ -released under NASA Open Source Agreement 1.3. Distribution must comply with \ -these agreements$(tput sgr0)) - ], - [TRICK_MONGOOSE="0"] - ), - [TRICK_MONGOOSE="0"] -) -AC_SUBST([TRICK_MONGOOSE]) - # If offline is specified, set some compilation flags AC_ARG_ENABLE([offline], AS_HELP_STRING([--enable-offline], [Compile Trick in offline mode. Un-tar the trick-offline directory in TRICK_HOME to use this feature]), @@ -372,6 +356,27 @@ AC_ARG_ENABLE([offline], ) AC_SUBST([TRICK_OFFLINE]) +AC_ARG_ENABLE([civet], + AS_HELP_STRING([--enable-civet], [Compile Trick with webserver capabilites.]), + AS_IF([test "x$enable_civet" = xyes], + [ + TRICK_DISABLE_CIVET="0" + ], + [TRICK_DISABLE_CIVET="1"] + ), + [TRICK_DISABLE_CIVET="0"] +) +AC_SUBST([TRICK_DISABLE_CIVET]) + +# If offline is specified, set some compilation flags +AC_ARG_ENABLE([offline], + AS_HELP_STRING([--enable-offline], [Compile Trick in offline mode. Un-tar the trick-offline directory in TRICK_HOME to use this feature]), + AS_IF([test "x$enable_offline" = xyes], + [TRICK_DISABLE_CIVET="1"], + ), +) +AC_SUBST([TRICK_OFFLINE]) + dnl look for programs we need to compile and run AC_PROG_AWK AC_PROG_CC diff --git a/configure b/configure index 046629d9..e92782f2 100755 --- a/configure +++ b/configure @@ -671,7 +671,7 @@ ac_ct_CC CFLAGS CC AWK -TRICK_MONGOOSE +TRICK_DISABLE_CIVET TRICK_OFFLINE TRICK_FORCE_32BIT LIBXML @@ -717,6 +717,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -742,7 +743,7 @@ enable_option_checking with_x enable_32bit enable_offline -enable_mongoose +enable_civet with_python with_prepend_path with_swig @@ -812,6 +813,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1064,6 +1066,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1201,7 +1212,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1354,6 +1365,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1395,7 +1407,7 @@ Optional Features: --enable-offline Compile Trick in offline mode. Un-tar the trick-offline directory in TRICK_HOME to use this feature - --enable-mongoose Compile Trick with webserver capabilites. + --enable-civet Compile Trick with webserver capabilites. --enable-java use java (default is yes) --enable-er7utils use er7_utils (default is yes) @@ -3875,30 +3887,6 @@ fi -# If offline is specified, set some compilation flags -# Check whether --enable-mongoose was given. -if test "${enable_mongoose+set}" = set; then : - enableval=$enable_mongoose; if test "x$enable_mongoose" = xyes; then : - - TRICK_MONGOOSE="1" - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \ -released under NASA Open Source Agreement 1.3. Distribution must comply with \ -these agreements$(tput sgr0)" >&5 -$as_echo "$as_me: WARNING: $(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \ -released under NASA Open Source Agreement 1.3. Distribution must comply with \ -these agreements$(tput sgr0)" >&2;} - -else - TRICK_MONGOOSE="0" - -fi -else - TRICK_MONGOOSE="0" - -fi - - - # If offline is specified, set some compilation flags # Check whether --enable-offline was given. if test "${enable_offline+set}" = set; then : @@ -3915,6 +3903,33 @@ fi +# Check whether --enable-civet was given. +if test "${enable_civet+set}" = set; then : + enableval=$enable_civet; if test "x$enable_civet" = xyes; then : + + TRICK_DISABLE_CIVET="0" + +else + TRICK_DISABLE_CIVET="1" + +fi +else + TRICK_DISABLE_CIVET="0" + +fi + + + +# If offline is specified, set some compilation flags +# Check whether --enable-offline was given. +if test "${enable_offline+set}" = set; then : + enableval=$enable_offline; if test "x$enable_offline" = xyes; then : + TRICK_DISABLE_CIVET="1" +fi +fi + + + for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. diff --git a/docs/documentation/web/Adding_SSL_encryption_to_Your_Webserver.md b/docs/documentation/web/Adding_SSL_encryption_to_Your_Webserver.md new file mode 100644 index 00000000..e4808818 --- /dev/null +++ b/docs/documentation/web/Adding_SSL_encryption_to_Your_Webserver.md @@ -0,0 +1,36 @@ +# Adding SSL Encryption to Your Webserver + +## Getting started ++ Install OpenSSL on your system. There are OpenSSL install packages for all major Linux distributions. ++ Create a SSL certificate ++ Edit your input file + +## Creating a SSL certificate +You can use your own certificate but to create a self signed certificate follow the steps below: +``` + openssl genrsa -des3 -out server.key 1024 + + openssl req -new -key server.key -out server.csr + + cp server.key server.key.orig + + openssl rsa -in server.key.orig -out server.key + + openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt + + cp server.crt server.pem + + cat server.key >> server.pem +``` + +## Edit your input file + +add the following lines to your input file +```python +web.server.ssl_enable = True +web.server.path_to_ssl_cert = "server.pem" +``` +Where server.pem is the path to the server.pem file you created when creating a SSL certificate + +## Access your webserver +Now that ssl encryption is enabled, to access you webserver use https://localhost.ssl:8888 instead of http and wss://localhost.ssl:8888 for http and ws protocals respectively. diff --git a/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.html b/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.html deleted file mode 100644 index 1ccf86de..00000000 --- a/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.html +++ /dev/null @@ -1,558 +0,0 @@ - - - - - - - -Adding_a_Web_Server_to_Your_Sim - - - - - - - - - - - -

Adding a Web Server to Your Sim

- -

To add a web server to your simulation, simply include the WebServer sim module into your S_define file:

- -
#include "sim_objects/WebServer.sm"
- -

Configuration of the Web Server

- -

The following (input.py) parameters are available to configure your web server:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Parameter NameDefault ValueDescription
web.server.enableFalseMust be explicitly enabled
web.server.port"8888"Web servers “listen” port
web.server.document_root"www"Web servers document root
web.server.debugFalsePrint Client/Server Communication.
- -

For your web server to be active, you must at least specify the following :

- -
web.server.enable = True
-
- -

To have your web server listen on port 8890, rather than 8888, you would specify:

- -
web.server.port = "8890"
- -

To serve files from a directory called my_document_root, rather than www:

- -
web.server.document_root = "my_document_root"
- -

To see client/server communication:

- -
web.server.debug = True
- -

When the Web Server Starts

- -

The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories.

- -

Connecting to Your Web Server

- -

Assuming that you accepted the default port, connect to http://localhost:8888/ from your web browser. This will display the index.html file in your root directory.

- -

The Default Document Root Directory

- -

The default document root directory that was initially created for you is minimal.

- -
www/
-    index.html
-    style.css
-    apps/
-    images/
- -

index.html is the file that’s displayed when you connect to http://localhost:8888/.

- -

style.css is a CSS style-sheet that’s included by index.html to give it some pizzazz.

- -

The apps directory contains links to some example html/javascript applications - in $TRICK_HOME/trick_source/web/apps.

- -

The images directory contains trick_icon.png.

- -

You are encouraged to add to, modify, and/or delete these files and directories to best suite the needs of your project.

- - - - - - - - - - - diff --git a/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.md b/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.md index 3cb78ee6..478dcaf3 100644 --- a/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.md +++ b/docs/documentation/web/Adding_a_Web_Server_to_Your_Sim.md @@ -1,30 +1,33 @@ -# Adding a Web Server to Your Sim +# Adding a Web Server to Your Sim -To add a web server to your simulation, simply include the WebServer sim module into your **S_define** file: +To add a web server to your simulation, simply include the CivetServer sim module into your **S_define** file: -``` -#include "sim_objects/WebServer.sm" +``` +#include "sim_objects/CivetServer.sm" ``` -## Configuration of the Web Server +## Configuration of the Web Server -The following (input.py) parameters are available to configure your web server: +The following (input.py) parameters are available to configure your web server: -|Parameter Name | Default Value| Description | -|------------------------|--------------|----------------------------------| -|web.server.enable | False |Must be explicitly enabled | -|web.server.port | "8888" |Web servers “listen” port | -|web.server.document_root| "www" |Web servers document root | -|web.server.debug | False |Print Client/Server Communication.| +|Parameter Name | Default Value | Description | +|---------------------------|---------------------------|-----------------------------------------------------------------| +|web.server.enable | False |Must be explicitly enabled | +|web.server.port | "8888" |Web servers “listen” port | +|web.server.document_root | "www" |Web servers document root | +|web.server.debug | False |Print Client/Server Communication. | +|web.server.ssl_enable | False |Encrypt traffic. Uses https instead of http. | +|web.server.path_to_ssl_cert|"~/.ssl/server.pem" |Path to your certificate. This is only used if ssl_enable = True| +|web.server.error_log_file | "civet_server_error.log" |CivetWeb error log file. | -For your web server to be active, you must at least specify the following : +For your web server to be active, you must at least specify the following : ```python -web.server.enable = True +web.server.enable = True ``` -To have your web server listen on port 8890, rather than 8888, you would specify: +To have your web server listen on port 8890, rather than 8888, you would specify: ```python web.server.port = "8890" @@ -32,8 +35,8 @@ web.server.port = "8890" To serve files from a directory called ```my_document_root```, rather than ```www```: -```python -web.server.document_root = "my_document_root" +```python +web.server.document_root = "my_document_root" ``` To see client/server communication: @@ -42,34 +45,34 @@ To see client/server communication: web.server.debug = True ``` -## When the Web Server Starts -The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories. +## When the Web Server Starts +The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories. -## Connecting to Your Web Server -Assuming that you accepted the default port, connect to ```http://localhost:8888/``` from your web browser. This will display the index.html file in your root directory. +## Connecting to Your Web Server +Assuming that you accepted the default port, connect to ```http://localhost:8888/``` (```https://localhost:8888/``` if ssl_enable=True) from your web browser. This will display the index.html file in your root directory. ## The Default Document Root Directory -The default document root directory that was initially created for you is minimal. +The default document root directory that was initially created for you is minimal. ``` -www/ - index.html - style.css - apps/ - images/ -``` +www/ + index.html + style.css + apps/ + images/ +``` -**index.html** is the file that’s displayed when you connect to http://localhost:8888/. +**index.html** is the file that’s displayed when you connect to http://localhost:8888/. -**style.css** is a CSS style-sheet that’s included by index.html to give it some pizzazz. +**style.css** is a CSS style-sheet that’s included by index.html to give it some pizzazz. -The **apps** directory contains links to some example html/javascript applications - in ```$TRICK_HOME/trick_source/web/apps```. +The **apps** directory contains links to some example html/javascript applications + in ```$TRICK_HOME/trick_source/web/apps```. -The **images** directory contains trick_icon.png. +The **images** directory contains trick_icon.png. -**You are encouraged to add to, modify, and/or delete these files and directories to best suite the needs of your project.** +**You are encouraged to add to, modify, and/or delete these files and directories to best suite the needs of your project.** diff --git a/docs/documentation/web/Extending_the_HTTP-API.html b/docs/documentation/web/Extending_the_HTTP-API.html deleted file mode 100644 index b2bd7d57..00000000 --- a/docs/documentation/web/Extending_the_HTTP-API.html +++ /dev/null @@ -1,583 +0,0 @@ - - - - - - - -Extending_the_HTTP-API - - - - - - - - - - - -

Extending the HTTP-API

- -

The HTTP-API is implemented as a collection of httpMethodHandlers. An httpMethodHandler is a pointer to a function that is expected to respond to an HTTP GET request, using the Cesanta Mongoose framework. An httpMethodHandler is defined (in trick/WebServer.hh) as follows:

- -
typedef void (*httpMethodHandler)(struct mg_connection*, struct http_message*);
- -

Documentation for the Cesanta Mongoose Networking Library can be found at: -https://cesanta.com/docs/overview/intro.html

- -

Example HTTP-API Extension

- -

Suppose you want your web server to send you a JSON message:

- -
{ "greeting" : "Hello Trick Sim Developer!" }
- -

when you invoke the URL: http://localhost:8888/api/http/hello.

- -

Creating an httpMethodHandler.

- -

The following two files will be our implementation of an httpMethodHandler. We'll put these in some models directory httpMethods/.

- -

handle_HTTP_GET_hello.h

- -
#ifndef HANDLE_HTTP_GET_HELLO
-#define HANDLE_HTTP_GET_HELLO
-
-#ifndef SWIG
-void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm);
-#endif
-
-#endif
- -

handle_HTTP_GET_hello.c

- -
#include "mongoose/mongoose.h"
-
-void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm) {
-    mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
-    const char* json_text =
-        "{ \"greeting\" : \"Hello Trick Sim Developer!\" }";
-    mg_printf_http_chunk(nc, "%s", json_text);
-    mg_send_http_chunk(nc, "", 0);
-}
- -

Installing our httpMethodHandler.

- -

We'll do this from our S_define file:

- - - - - -
web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello );
- -

A Complete S_define

- -
/***********************TRICK HEADER*************************
-PURPOSE:
-    (Cannon Numeric)
-LIBRARY DEPENDENCIES:
-    (
-     (cannon/gravity/src/cannon_init.c)
-     (cannon/gravity/src/cannon_numeric.c)
-     (httpMethods/handle_HTTP_GET_hello.c)
-    )
-*************************************************************/
-
-#include "sim_objects/default_trick_sys.sm"
-#include "sim_objects/WebServer.sm"
-##include "cannon/gravity/include/cannon_numeric.h"
-##include "httpMethods/handle_HTTP_GET_hello.h"
-
-class CannonSimObject : public Trick::SimObject {
-
-    public:
-        CANNON      cannon ;
-        int foo;
-        CannonSimObject() {
-            ("default_data") cannon_default_data( &cannon ) ;
-            ("initialization") cannon_init( &cannon ) ;
-            ("derivative") cannon_deriv( &cannon ) ;
-            ("integration") trick_ret = cannon_integ( &cannon ) ;
-            ("dynamic_event") cannon_impact( &cannon) ;
-        }
-} ;
-CannonSimObject dyn ;
-
-IntegLoop dyn_integloop (0.01) dyn;
-
-void create_connections() {
-    dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
-    web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello );
-}
-
- - - - - - - - - - - - - - - - - diff --git a/docs/documentation/web/Extending_the_HTTP-API.md b/docs/documentation/web/Extending_the_HTTP-API.md index 53cc69bd..8e82078c 100644 --- a/docs/documentation/web/Extending_the_HTTP-API.md +++ b/docs/documentation/web/Extending_the_HTTP-API.md @@ -1,77 +1,79 @@ -##Extending the HTTP-API - -The HTTP-API is implemented as a collection of ```httpMethodHandlers```. An ```httpMethodHandler``` is a pointer to a function that is expected to respond to an HTTP GET request, using the **Cesanta Mongoose** framework. An ```httpMethodHandler``` is defined (in ```trick/WebServer.hh```) as follows: - - - - +## Extending the HTTP-API -```c -typedef void (*httpMethodHandler)(struct mg_connection*, struct http_message*); -``` - -Documentation for the **Cesanta Mongoose Networking Library** can be found at: -[https://cesanta.com/docs/overview/intro.html](https://cesanta.com/docs/overview/intro.html) - -## Example HTTP-API Extension - -Suppose you want your web server to send you a JSON message: - -```json -{ "greeting" : "Hello Trick Sim Developer!" } -``` - -when you invoke the URL: ```http://localhost:8888/api/http/hello```. - -### Creating an ```httpMethodHandler```. - -The following two files will be our implementation of an ```httpMethodHandler```. We'll put these in some models directory ```httpMethods/```. - -**```handle_HTTP_GET_hello.h```** +The HTTP-API is implemented as a collection of ```httpMethodHandlers```. An ```httpMethodHandler``` is a pointer to a function that is expected to respond to an HTTP GET request, using the **CivetWeb** framework. An ```httpMethodHandler``` is defined (in ```trick/CivetWeb.hh```) as follows: + + + + + +```c +typedef void (*httpMethodHandler)(struct mg_connection *, void* cbdata); +``` + +Documentation for the **CivetWeb Networking Library** can be found at: +[https://cesanta.com/docs/overview/intro.html](http://civetweb.github.io/civetweb/) + +## Example HTTP-API Extension + +Suppose you want your web server to send you a JSON message: + +```json +{ "greeting" : "Hello Trick Sim Developer!" } +``` + +when you invoke the URL: ```http://localhost:8888/api/http/hello```. + +### Creating an ```httpMethodHandler```. + +The following two files will be our implementation of an ```httpMethodHandler```. We'll put these in some models directory ```httpMethods/```. + +**```handle_HTTP_GET_hello.h```** ```c #ifndef HANDLE_HTTP_GET_HELLO #define HANDLE_HTTP_GET_HELLO #ifndef SWIG -void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm); +void handle_HTTP_GET_hello(struct mg_connection *nc, void *hm); #endif -#endif +#endif ``` - -**```handle_HTTP_GET_hello.c```** - -```c -#include "mongoose/mongoose.h" -void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm) { +**```handle_HTTP_GET_hello.c```** + +```c +#include "civet/CivetServer.h" +#include "civet/civetweb.h" +#include + +void handle_HTTP_GET_hello(struct mg_connection *nc, void *hm) { mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); const char* json_text = "{ \"greeting\" : \"Hello Trick Sim Developer!\" }"; - mg_printf_http_chunk(nc, "%s", json_text); - mg_send_http_chunk(nc, "", 0); -} -``` - -### Installing our ```httpMethodHandler```. - -We'll do this from our **S_define** file: - -* Add ```(httpMethods/handle_HTTP_GET_hello.c)``` to the ```LIBRARY DEPENDENCIES```. - -* Include our header file: - - ```##include "httpMethods/handle_HTTP_GET_hello.h"``` - -* In ```create_connections()``` add : - -```c -web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); -``` -### A Complete S_define - -```c++ + mg_send_chunk(nc, json_text, strlen(json_text)); + mg_send_chunk(nc, "", 0); +} +``` + +### Installing our ```httpMethodHandler```. + +We'll do this from our **S_define** file: + +* Add ```(httpMethods/handle_HTTP_GET_hello.c)``` to the ```LIBRARY DEPENDENCIES```. + +* Include our header file: + + ```##include "httpMethods/handle_HTTP_GET_hello.h"``` + +* In ```create_connections()``` add : + +```c +web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); +``` +### A Complete S_define + +```c++ /***********************TRICK HEADER************************* PURPOSE: (Cannon Numeric) @@ -84,7 +86,7 @@ LIBRARY DEPENDENCIES: *************************************************************/ #include "sim_objects/default_trick_sys.sm" -#include "sim_objects/WebServer.sm" +#include "sim_objects/CivetServer.sm" ##include "cannon/gravity/include/cannon_numeric.h" ##include "httpMethods/handle_HTTP_GET_hello.h" @@ -109,5 +111,5 @@ void create_connections() { dyn_integloop.getIntegrator(Runge_Kutta_4, 5); web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); } - -``` + +``` diff --git a/docs/documentation/web/Extending_the_WS-API.html b/docs/documentation/web/Extending_the_WS-API.html deleted file mode 100644 index c91b9ee9..00000000 --- a/docs/documentation/web/Extending_the_WS-API.html +++ /dev/null @@ -1,736 +0,0 @@ - - - - - - - -Extending_the_WS-API - - - - - - - - - - - -

Extending the WebSocket-API

- -

When You Create a WebSocket Connection

- -

Consider the following Javascript, that creates a web socket connection:

- -

var ws = new WebSocket('ws://localhost:8888/api/ws/VariableServer');

- -

In the URL: ws://localhost:8888/api/ws/VariableServer

- - - -

In the Trick web server, the path associated with a websocket must begin with -/api/ws/. The remaining part of the path, i.e., VariableServer is the key that specifies the sub-protocol, prescribing what messages will be passed between client and server, and what those messages mean.

- -

When a web-socket connection is established, the key will determine what type (sub-class) of WebSocketSession object to create, to manage the connection.

- -

WebSocketSession

- -

A WebSocketSession is a pure virtual base class meant to represent the state of one of potentially many websocket connections. It provides methods to:

- -
    -
  1. Synchronously marshall Trick simulation data for out-going messages
  2. -
  3. Send messages to the websocket client, and
  4. -
  5. Receive and process messages from the websocket client.
  6. -
- -

To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. WebSocketSession.hh can be found in ${TRICK_HOME}/include/trick/.

- -

WebSocketSession.hh

- -
/*************************************************************************
-PURPOSE: (Represent Websocket connection.)
-**************************************************************************/
-#ifndef WEB_SOCKET_SESSION_HH
-#define WEB_SOCKET_SESSION_HH
-
-#include <string>
-#ifndef SWIG
-#include "mongoose/mongoose.h"
-#endif
-
-class WebSocketSession {
-    public:
-        WebSocketSession(struct mg_connection *nc):connection(nc){};
-        virtual ~WebSocketSession() {};
-        virtual void marshallData()=0;
-        virtual void sendMessage()=0;
-        virtual int  handleMessage(std::string)=0;
-
-        struct mg_connection* connection;
-};
-#endif
- -

Adding Your New WebSocketSession Type to the WebServer

- -

To install your new websocket protocol, you'll need to create a function that -creates an instance of your new WebSocketSession type. Then you'll need to call -HTTP_Server::installWebSocketSessionMaker to install the function, with a -label.

- -

The function you'll create will take struct mg_connection * as an argument -and return WebSocketSession*.

- -

Example

- -

Let's create a new web socket protocol that sends the time in GMT or local time.

- -

First we'll derive a new type called TimeSession from WebSocketSession.

- -

TimeSession.hh

- -
/*************************************************************************
-PURPOSE: (Represent the state of a variable server websocket connection.)
-**************************************************************************/
-#ifndef TIMESESSION_HH
-#define TIMESESSION_HH
-#include <vector>
-#include <string>
-#include "time.h"
-#include "trick/WebSocketSession.hh"
-
-class TimeSession : public WebSocketSession {
-    public:
-        enum Zone { GMT, LOCAL};
-        TimeSession(struct mg_connection *nc);
-        ~TimeSession();
-        void marshallData();
-        void sendMessage();
-        int  handleMessage(std::string);
-    private:
-        time_t now;
-        Zone zone;
-};
-
-WebSocketSession* makeTimeSession( struct mg_connection *nc );
-#endif
- -

Below is our implementation. Notice the function makeTimeSession at the bottom.

- -

TimeSession.cpp

- -
#include <stdio.h>
-#include <time.h>
-#include <iostream>
-#include "TimeSession.hh"
-
-// CONSTRUCTOR
-TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) {
-    time(&now);
-}
-
-// DESTRUCTOR
-TimeSession::~TimeSession() {}
-
-void TimeSession::marshallData() {
-    time(&now);
-}
-
-void TimeSession::sendMessage() {
-
-    char message[1024];
-    struct tm *theTime;
-    if (zone == TimeSession::LOCAL) {
-        theTime = localtime(&now);
-    } else {
-        theTime = gmtime(&now);
-    }
-    int hours   = theTime->tm_hour;
-    int minutes = theTime->tm_min;
-    int seconds = theTime->tm_sec;
-    int day     = theTime->tm_mday;
-    int month   = theTime->tm_mon + 1;
-    int year    = theTime->tm_year + 1900;
-
-    sprintf(message, "Time: %02d:%02d:%02d Date: %02d/%02d/%d\n", hours, minutes, seconds, month, day, year);
-    mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
-}
-
-int TimeSession::handleMessage(std::string client_msg) {
-
-   if (client_msg.compare("GMT") == 0) {
-       zone = TimeSession::GMT;
-   } else if (client_msg.compare("LOCAL") == 0) {
-       zone = TimeSession::LOCAL;
-   } else {
-      std::cerr << "ERROR: Unknown command \"" << client_msg << "\"." << std::endl;
-   }
-   return 0;
-}
-
-// WebSocketSessionMaker function for a TimeSession.
-WebSocketSession* makeTimeSession( struct mg_connection *nc ) {
-    std::cerr << "DEBUG: Creating new TimeSession." << std::endl;
-    return new TimeSession(nc);
-}
- -

We put TimeSession.cpp and TimeSession.cpp into a models directory called httpMethods/.

- -

S_define Modifications

- -
    -
  1. Specify the dependency on the httpMethods/TimeSession.cpp compilation unit.
  2. -
  3. We should already be including the WebServer sim object, otherwise we don't even have a webserver.
  4. -
  5. We need to include our new header file: ##include "httpMethods/TimeSession.hh"
  6. -
  7. Finally, install our WebSocketSession type: web.server.installWebSocketSessionMaker("Time", &makeTimeSession); -The label we use for our protocol here is "Time", but it can be whatever name you choose.
  8. -
- -
/***********************TRICK HEADER*************************
-PURPOSE:
-    (Cannon Numeric)
-LIBRARY DEPENDENCIES:
-    (
-     (cannon/gravity/src/cannon_init.c)
-     (cannon/gravity/src/cannon_numeric.c)
-     (httpMethods/TimeSession.cpp)                                    // <--(1)
-    )
-*************************************************************/
-
-#include "sim_objects/default_trick_sys.sm"
-#include "sim_objects/WebServer.sm"                                   // <--(2)
-##include "cannon/gravity/include/cannon_numeric.h"
-##include "httpMethods/TimeSession.hh"                                // <--(3)
-
-class CannonSimObject : public Trick::SimObject {
-
-    public:
-        CANNON      cannon ;
-        int foo;
-        CannonSimObject() {
-            ("default_data") cannon_default_data( &cannon ) ;
-            ("initialization") cannon_init( &cannon ) ;
-            ("derivative") cannon_deriv( &cannon ) ;
-            ("integration") trick_ret = cannon_integ( &cannon ) ;
-            ("dynamic_event") cannon_impact( &cannon) ;
-        }
-} ;
-CannonSimObject dyn ;
-
-IntegLoop dyn_integloop (0.10) dyn;
-
-void create_connections() {
-    dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
-    web.server.installWebSocketSessionMaker("Time", &makeTimeSession);  // <--(4)
-}
- -

Testing The New WebSocket Interface

- -

To test your new web socket interface, put the following time.html file in $YOUR_SIM_DIRECTORY/www/apps. Then request http://localhost:8888/apps/time.html from your browser. You should see the time messages from your sim.

- -

time.html

- -
<!DOCTYPE html>
-<html>
-  <head>
-    <title>WS Example</title>
-  </head>
-  <body>
-      <div id="output"></div>
-      <script type="text/javascript">
-
-          function log(s) {
-              var p = document.createElement("p");
-              p.style.wordWrap = "break-word";
-              p.textContent = s;
-              output.appendChild(p);
-          }
-
-          var ws = new WebSocket('ws://localhost:8888/api/ws/Time');
-
-          // WebSocket Event Handlers
-          ws.onopen = function(e) {
-              ws.send("GMT");
-          };
-          ws.onmessage = function(e) {
-             log(e.data);
-          };
-          ws.onerror = function(e) {
-             console.log("WebSocket Error: " , e);
-             handleErrors(e);
-          };
-          ws.onclose = function(e) {
-             console.log("Connection closed", e);
-          };
-
-      </script>
-  </body>
-</html>
- - - - - - - - - - - - - - - - - diff --git a/docs/documentation/web/Extending_the_WS-API.md b/docs/documentation/web/Extending_the_WS-API.md index 7faf00f5..21da130d 100644 --- a/docs/documentation/web/Extending_the_WS-API.md +++ b/docs/documentation/web/Extending_the_WS-API.md @@ -1,4 +1,4 @@ -#Extending the WebSocket-API +## Extending the WebSocket-API ## When You Create a WebSocket Connection @@ -23,9 +23,9 @@ A ```WebSocketSession``` is a pure virtual base class meant to represent the sta 1. Synchronously marshall Trick simulation data for out-going messages 2. Send messages to the websocket client, and - 3. Receive and process messages from the websocket client. - -To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. ```WebSocketSession.hh``` can be found in ```${TRICK_HOME}/include/trick/```. + 3. Receive and process messages from the websocket client. + +To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. ```WebSocketSession.hh``` can be found in ```${TRICK_HOME}/include/trick/```. ### WebSocketSession.hh ```c @@ -37,13 +37,19 @@ PURPOSE: (Represent Websocket connection.) #include #ifndef SWIG -#include "mongoose/mongoose.h" +#include "civet/CivetServer.h" #endif class WebSocketSession { public: WebSocketSession(struct mg_connection *nc):connection(nc){}; virtual ~WebSocketSession() {}; + + /** + When HTTP_Server::time_homogeneous is set, WebSocketSession::marshallData() is called from the main + sim thread in a "top_of_frame" job, so that all of the data can be staged at + the same sim-time, in other words it's time-homogeneous. + */ virtual void marshallData()=0; virtual void sendMessage()=0; virtual int handleMessage(std::string)=0; @@ -109,6 +115,7 @@ Below is our implementation. Notice the function ```makeTimeSession``` at the bo #include #include #include "TimeSession.hh" +#include // CONSTRUCTOR TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) { @@ -139,7 +146,7 @@ void TimeSession::sendMessage() { int year = theTime->tm_year + 1900; sprintf(message, "Time: %02d:%02d:%02d Date: %02d/%02d/%d\n", hours, minutes, seconds, month, day, year); - mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message)); + mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message)); } int TimeSession::handleMessage(std::string client_msg) { @@ -180,14 +187,15 @@ LIBRARY DEPENDENCIES: ( (cannon/gravity/src/cannon_init.c) (cannon/gravity/src/cannon_numeric.c) - (httpMethods/TimeSession.cpp) // <--(1) + (httpMethods/handle_HTTP_GET_hello.c) + (httpMethods/TimeSession.cpp) ) *************************************************************/ #include "sim_objects/default_trick_sys.sm" -#include "sim_objects/WebServer.sm" // <--(2) +#include "sim_objects/CivetServer.sm" ##include "cannon/gravity/include/cannon_numeric.h" -##include "httpMethods/TimeSession.hh" // <--(3) +##include "httpMethods/TimeSession.hh" class CannonSimObject : public Trick::SimObject { @@ -204,11 +212,12 @@ class CannonSimObject : public Trick::SimObject { } ; CannonSimObject dyn ; -IntegLoop dyn_integloop (0.10) dyn; +IntegLoop dyn_integloop (0.01) dyn; void create_connections() { dyn_integloop.getIntegrator(Runge_Kutta_4, 5); - web.server.installWebSocketSessionMaker("Time", &makeTimeSession); // <--(4) + web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); + web.server.installWebSocketSessionMaker( "Time", &makeTimeSession ); } ``` diff --git a/docs/documentation/web/HTTP-API_alloc_info.html b/docs/documentation/web/HTTP-API_alloc_info.html deleted file mode 100644 index 47f133fe..00000000 --- a/docs/documentation/web/HTTP-API_alloc_info.html +++ /dev/null @@ -1,633 +0,0 @@ - - - - - - - -HTTP-API_alloc_info - - - - - - - - - - - -

HTTP-API: alloc_info

- -

http://localhost:8888/api/http/alloc_info

- -

Purpose

- -

Request a JSON encoded sub-list of allocation descriptors from the Memory Manager’s alloc_info list.

- -

Query String Parameters

- - - - - - - - - - - - - - - - - - - - - - -
ParameterDefaultDescription
start0starting index of the sub-list.
count20number of allocation descriptors.
- -

EXAMPLE:

- -

http://localhost:8888/api/http/alloc_info?start=20&count=2

- -

Query Response

- -

Returns a JSON object containing four name-value pairs:

- -

JSON Response Object

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameValue Description
alloc_totalTotal number allocations in the Memory Manager’s alloc_info list.
chunk_sizeNumber of allocation description objects in alloc_list.
chunk_startThe Memory Manager alloc_info index of the first alloc_list element below
alloc_listArray of JSON Allocation Description objects (described below).
- -

JSON Allocation Description Object

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameValue Description
nameName of the allocation. May be Null
startStarting address of the allocation.
endEnding address of the allocation.
num
sizeSize of the allocation in bytes.
typeType descriptor of the allocation.
stclStorage class of the allocation. Either TRICK_EXTERN or TRICK_LOCAL.
languageLanguage. Either : Language_C or Language_CPP.
indexArray dimension sizes of the allocation (if it represents an array).
- -

Example:

- -

In SIM_cannon_numeric (one of Trick's example sims) the following query resulted in the following JSON.

- -

Query

- -

http://localhost:8888/api/http/alloc_info?start=20&count=2

- -

Response

- -
{ "alloc_total":43,
-  "chunk_size":2,
-  "chunk_start":20,
-  "alloc_list":[
-                 { "name":"dyn",
-                   "start":"0x101aa9900",
-                   "end":"0x101aa9b27",
-                   "num":"1",
-                   "size":"552",
-                   "type":"CannonSimObject",
-                   "stcl":"TRICK_EXTERN",
-                   "language":"Language_CPP",
-                   "index": []
-                 }
-                 ,
-                 { "name":"web",
-                   "start":"0x101aa9610",
-                   "end":"0x101aa98ff",
-                   "num":"1",
-                   "size":"752",
-                   "type":"WebServerSimObject",
-                   "stcl":"TRICK_EXTERN",
-                   "language":"Language_CPP",
-                   "index": []
-                 }
-               ]
-}
-
- - - - - - - - - - - - - diff --git a/docs/documentation/web/WS-API_VariableServer.html b/docs/documentation/web/WS-API_VariableServer.html deleted file mode 100644 index c55b2320..00000000 --- a/docs/documentation/web/WS-API_VariableServer.html +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - -WS-API_VariableServer - - - - - - - - - - - -

WS-API: VariableServer

- -

ws://localhost:8888/api/ws/VariableServer

- -

Purpose

- -

JSON Variable Server

- -

Client to Server Command Messages

- -

Add a Trick Variable to the current session.

- -
{ "cmd" : "var_add",
-  "var_name" : string
-}
- -

Stop sending periodic var_list messages (see below) from the server.

- -
{ "cmd" : "var_pause" }
- -

Resume sending periodic var_list response messages from the server.

- -
{ "cmd" : "var_unpause" }
-
- -

Send one var_list message from the server.

- -
{ "cmd" : "var_send" }
- -

Clear all variables from the current session, that is: undo all of the var_add commands.

- -
{ "cmd" : "var_clear" }
- -

Disconnect from the variable server.

- -
{ "cmd" : "var_exit" }
- -

Set the period (in milliseconds) at which var_list messages are sent form the server.

- -
{ "cmd" : "var_cycle",
-  "period" : integer
-}
- -

Execute the given Python code in the host sim.

- -
{ "cmd" : "python",
-  "pycode" : string
-}
- -

Send the sie structure from the server. Response will be the sie response message (below).

- -
{ "cmd" : "sie" }
- -

Send the units for the given variable. Response will be the units response message (below).

- -
{ "cmd" : "units",
-  "var_name" : string
-}
- -

Server to Client Response Messages

- -

Error Response

- -
{ "msg_type" : "error",
-  "error_text" : string
-}
- -

Periodic response containing the values of variables requested by var_add.

- -
{ "msg_type" : "var_list"
-  "time" : double
-  "values" : []
-}
- -

Response to the sie command (above).

- -
{ "msg_type" : "sie",
-  "data" : string
-}
- -

Response to the units command (above).

- -
{ "msg_type" : "units",
-  "var_name" : string,
-  "data" : string
-}
- -

Example Variable Server Client

- -
<!DOCTYPE html>
-<html>
-  <head>
-    <title>WS Experiments</title>
-  </head>
-  <body>
-      <style>
-          table { border-collapse: collapse; width: 100%; }
-          th, td { text-align: left; padding: 8px; }
-          tr:nth-child(even){background-color: #f2f2f2}
-          th { background-color: #562399; color: white; }
-      </style>
-      <header>
-      </header>
-
-      <div class="variableDisplay"></div>
-      <table class="variables">
-          <tr>
-              <th>Variable</th>
-              <th>Value</th>
-          </tr>
-      </table>
-
-      <div id="output"></div>
-      <script type="text/javascript">
-          function log(s) {
-              var p = document.createElement("p");
-              p.style.wordWrap = "break-word";
-              p.textContent = s;
-              output.appendChild(p);
-          }
-          function sendMessage(msg) {
-              ws.send(msg);
-          }
-          // Interface to Trick WebSocket Variable Server
-          function setPeriod(period) {
-              sendMessage(`{"cmd":"var_cycle","period":${period}}`);
-          }
-          function addVarTableRow(name, value) {
-              // create a row in the table that contains two <td>s, one for the var_name and one for its value.
-              let tr = document.createElement('tr');
-              let td1 = document.createElement('td');
-              td1.textContent = `${name}`;
-              let td2 = document.createElement('td');
-              td2.textContent = `${value}`;
-              td2.className = "values";
-              tr.appendChild(td1);
-              tr.appendChild(td2);
-              varTable.appendChild(tr);
-          }
-          function addVariable(name, value) {
-              sendMessage(`{"cmd":"var_add","var_name": "${name}"}`);
-              addVarTableRow(name, value);
-          }
-          var varTable = document.querySelector('table.variables');
-
-
-          var ws = new WebSocket('ws://localhost:8888/api/ws/VariableServer');
-          ws.onopen = function(e) {
-              setPeriod(100);
-              addVarTableRow("Time", 0.0);
-              addVariable("dyn.cannon.pos[0]", 0.0);
-              addVariable("dyn.cannon.pos[1]", 0.0);
-              addVariable("dyn.cannon.vel[0]", 0.0);
-              addVariable("dyn.cannon.vel[1]", 0.0);
-              addVariable("dyn.cannon.time", 0.0);
-              addVariable("dyn.cannon.timeRate", 0.0);
-              addVariable("dyn.cannon.impact", 0.0);
-              addVariable("I.dont.exist", 0.0);
-              sendMessage("{\"cmd\":\"var_unpause\"}");
-          };
-          ws.onmessage = function(e) {
-             let msg = JSON.parse(e.data);
-             if (msg.msg_type == "values") {
-                 let valueNodes = varTable.getElementsByClassName("values");
-                 valueNodes[0].textContent = msg.time;
-                 for (let i = 0; i < msg.values.length; i++ ) {
-                     valueNodes[i+1].textContent = msg.values[i];
-                 }
-             }
-          };
-          ws.onerror = function(e) {
-             console.log("WebSocket Error: " , e);
-             handleErrors(e);
-          };
-          ws.onclose = function(e) {
-             console.log("Connection closed", e);
-          };
-
-      </script>
-  </body>
-</html>
- - - - - - - - - - - - - - - diff --git a/docs/documentation/web/index.html b/docs/documentation/web/index.html deleted file mode 100644 index 85547ad3..00000000 --- a/docs/documentation/web/index.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - Web Server Documentation -
- - - -

Web Server Documentation

-
- - - - -
-
- - - diff --git a/include/trick/MyCivetServer.hh b/include/trick/MyCivetServer.hh new file mode 100644 index 00000000..086f265d --- /dev/null +++ b/include/trick/MyCivetServer.hh @@ -0,0 +1,85 @@ +/************************************************************************* +PURPOSE: (Represent the state and initial conditions of an http server.) +**************************************************************************/ +#ifndef CIVET_SERVER_H +#define CIVET_SERVER_H + +#ifdef USE_CIVET + +#include +#include +#include +#include +#include +#include +#include "trick/WebSocketSession.hh" + +typedef WebSocketSession* (*WebSocketSessionMaker)(struct mg_connection *nc); +typedef void (*httpMethodHandler)(struct mg_connection *, void* cbdata); + +class MyCivetServer { + public: + + unsigned int port; + const char* document_root; + bool enable; + bool debug; + bool time_homogeneous; + const char* path_to_ssl_cert; + bool ssl_enable; + const char* error_log_file; + + struct mg_context *ctx; /* ** civetweb */ + + // Trick Job-Functins + int default_data(); + int shutdown(); + int init(); + int join(); + int http_top_of_frame(); + + //TODO: Make these private and fix threading design issue + pthread_t server_thread; /* ** */ + bool sessionDataMarshalled; /* ** */ + pthread_mutex_t lock_loop; /* ** */ + pthread_cond_t cond_loop; /* ** */ + bool service_connections = true; /* ** */ + bool shutting_down; /* ** */ + + std::map WebSocketSessionMakerMap; /* ** */ + pthread_mutex_t WebSocketSessionMakerMapLock; /* ** */ + + std::map webSocketSessionMap; /* ** */ + pthread_mutex_t WebSocketSessionMapLock; /* ** */ + + std::map< std::string, httpMethodHandler> httpGETHandlerMap; /* ** */ + pthread_mutex_t httpGETHandlerMapLock; /* ** */ + + void addWebSocketSession(struct mg_connection *nc, WebSocketSession* session); + WebSocketSession* makeWebSocketSession(struct mg_connection *nc, std::string name); + void marshallWebSocketSessionData(); + void sendWebSocketSessionMessages(struct mg_connection *nc); + void unlockConnections(); + void deleteWebSocketSession(struct mg_connection * nc); + void installHTTPGEThandler(std::string handlerName, httpMethodHandler handler); + void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker); + void handleWebSocketClientMessage(struct mg_connection *conn, const char* data); + void handleHTTPGETrequest(struct mg_connection *conn, const struct mg_request_info* ri, std::string handlerName); + + + std::string tmp_string; + + + + // void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker); + + +}; + +struct Data { + MyCivetServer* server; + std::string name; +}; + +#endif +#endif diff --git a/include/trick/WebServer.hh b/include/trick/WebServer.hh deleted file mode 100644 index 14688231..00000000 --- a/include/trick/WebServer.hh +++ /dev/null @@ -1,69 +0,0 @@ -/************************************************************************* -PURPOSE: (Represent the state and initial conditions of an http server.) -**************************************************************************/ -#ifndef WEB_SERVER_H -#define WEB_SERVER_H - -#ifdef USE_MONGOOSE - -#include -#include -#include -#ifndef SWIG -#include "mongoose/mongoose.h" -#endif -#include "trick/WebSocketSession.hh" - -typedef void (*httpMethodHandler)(struct mg_connection *, struct http_message *); -typedef WebSocketSession* (*WebSocketSessionMaker)(struct mg_connection *nc); - -class WebServer { - public: - const char* port; - const char* document_root; - struct mg_mgr mgr; /* ** mongoose */ - struct mg_connection *listener; /* ** mongoose */ - pthread_t server_thread; /* ** */ - bool shutting_down; - bool enable; - bool debug; - - std::map< std::string, httpMethodHandler> httpGETHandlerMap; /* ** */ - pthread_mutex_t httpGETHandlerMapLock; /* ** */ - - std::map< std::string, WebSocketSessionMaker> WebSocketSessionMakerMap; /* ** */ - pthread_mutex_t WebSocketSessionMakerMapLock; /* ** */ - - std::map webSocketSessionMap; /* ** */ - pthread_mutex_t webSocketSessionMapLock; /* ** */ - - pthread_mutex_t serviceLock; /* ** */ - struct mg_serve_http_opts http_server_options; /* ** */ - struct mg_bind_opts bind_opts; /* ** */ - pthread_cond_t serviceConnections; /* ** */ - bool service_websocket; - bool time_homogeneous; - bool sessionDataMarshalled; - - // Trick Job-functions - int http_default_data(); - int http_init(); - int http_top_of_frame(); - int http_shutdown(); - - void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker); - void installHTTPGEThandler(std::string handlerName, httpMethodHandler handler); - - // These are internals, and should not be considered public. They are not private only - // because they need to be callable from the servers event handler. - void sendWebSocketSessionMessages(struct mg_connection *nc); - void handleWebSocketClientMessage(struct mg_connection *nc, std::string msg); - void addWebSocketSession(struct mg_connection *nc, WebSocketSession* session); - void deleteWebSocketSession(struct mg_connection *nc); - WebSocketSession* makeWebSocketSession(struct mg_connection *nc, std::string name); - void handleHTTPGETrequest(struct mg_connection *nc, http_message *hm, std::string handlerName); - void marshallWebSocketSessionData(); -}; - -#endif -#endif diff --git a/include/trick/WebSocketSession.hh b/include/trick/WebSocketSession.hh index 389e33f5..a7f6ea80 100644 --- a/include/trick/WebSocketSession.hh +++ b/include/trick/WebSocketSession.hh @@ -4,10 +4,10 @@ PURPOSE: (Represent Websocket connection.) #ifndef WEB_SOCKET_SESSION_HH #define WEB_SOCKET_SESSION_HH -#ifdef USE_MONGOOSE +#ifdef USE_CIVET #include #ifndef SWIG -#include "mongoose/mongoose.h" +#include "civet/CivetServer.h" #endif class WebSocketSession { @@ -28,4 +28,4 @@ class WebSocketSession { }; #endif -#endif +#endif \ No newline at end of file diff --git a/include/trick/files_to_ICG.hh b/include/trick/files_to_ICG.hh index 161f8fb1..fde05cd6 100644 --- a/include/trick/files_to_ICG.hh +++ b/include/trick/files_to_ICG.hh @@ -97,8 +97,8 @@ #include "trick/lqueue.h" #include "trick/lstack.h" -#ifdef USE_MONGOOSE -#include "trick/WebServer.hh" +#ifdef USE_CIVET +#include "trick/MyCivetServer.hh" #include "trick/WebSocketSession.hh" #endif diff --git a/share/trick/makefiles/Makefile.common b/share/trick/makefiles/Makefile.common index 43d60124..0bf65c20 100644 --- a/share/trick/makefiles/Makefile.common +++ b/share/trick/makefiles/Makefile.common @@ -171,11 +171,13 @@ ifneq ($(GSL_HOME),) TRICK_SYSTEM_CXXFLAGS += -D_HAVE_GSL endif -ifeq (${TRICK_MONGOOSE},1) - TRICK_LIBS += -ltrickHTTP ${TRICK_LIB_DIR}/libmongoose.a - TRICK_SWIG_FLAGS += -DUSE_MONGOOSE - TRICK_SYSTEM_CXXFLAGS += -DUSE_MONGOOSE - TRICK_ICG_EXCLUDE += :${TRICK_HOME}/include/mongoose +TRICK_CIVET=0 +ifneq (${TRICK_DISABLE_CIVET},1) + TRICK_CIVET=1 + TRICK_LIBS += ${TRICK_LIB_DIR}/libcivetweb.a ${TRICK_LIB_DIR}/libtrickCivet.a + TRICK_SWIG_FLAGS += -DUSE_CIVET + TRICK_SYSTEM_CXXFLAGS += -DUSE_CIVET + TRICK_ICG_EXCLUDE += ${TRICK_HOME}/include/civet endif # We pipe the output of compiler through tee. If the user wanted gcc color, make sure they get it. diff --git a/share/trick/makefiles/config_user.mk.in b/share/trick/makefiles/config_user.mk.in index 2fe8610b..52ffedda 100644 --- a/share/trick/makefiles/config_user.mk.in +++ b/share/trick/makefiles/config_user.mk.in @@ -17,7 +17,7 @@ USE_JAVA = @USE_JAVA@ JAVAC = @JAVA_CC@ TRICK_OFFLINE = @TRICK_OFFLINE@ -TRICK_MONGOOSE = @TRICK_MONGOOSE@ +TRICK_DISABLE_CIVET = @TRICK_DISABLE_CIVET@ USE_X_WINDOWS = @USE_X_WINDOWS@ diff --git a/share/trick/pymods/trick/.gitignore b/share/trick/pymods/trick/.gitignore new file mode 100644 index 00000000..1c39d874 --- /dev/null +++ b/share/trick/pymods/trick/.gitignore @@ -0,0 +1,6 @@ +*venv +.vscode/* +*.pem +*.key +*.crt +*.csr diff --git a/share/trick/pymods/trick/conftest.py b/share/trick/pymods/trick/conftest.py new file mode 100644 index 00000000..b3961b6c --- /dev/null +++ b/share/trick/pymods/trick/conftest.py @@ -0,0 +1,68 @@ +import pytest +import sys +import os +from typing import Dict, Tuple +import subprocess +import inspect + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda:0))), '../..'))) +from utils import is_web_server_started, params, pause + +# store history of failures per test class name and per index in parametrize (if parametrize used) +web_server_status = {} +web_server_status2 = None + +def build_sim(): + with open(os.path.join(params.get_path_to_sim(), params.get_input_folder(), params.get_test_input_file()), "w") as f: + f.write( \ + f"""web.server.enable = True +web.server.debug = False +web.server.ssl_enable = {params.get_ssl_enable()} +web.server.path_to_ssl_cert = '{params.get_ssl_cert_path()}' +web.server.port = {params.get_port()} + +trick.var_server_set_port({params.get_var_server_port()}) + +trick.frame_log_on() +trick.real_time_enable() +trick.exec_set_software_frame(0.1) +trick.itimer_enable() + +trick.exec_set_enable_freeze(True) +trick.exec_set_freeze_command(True)""") + + pathToSim=params.get_path_to_sim() + if params.get_build_sim(): + #TODO: Need make file to only rebuild only when necessary, otherwise, test need to rebuild and this is time consuming. + print("#"*10) + print("Auto rebuilding sim. Auto rebuild will build the SIM everytime the test is run, which can take some time.") + print("To turn auto rebuild off, in utils.py, self.__build_sim = False. Note: it's important that SIM rebuild is current.") + print("#"*10) + + build_cmd = f"echo \"cd {pathToSim} && make -C {params.get_trick_home()}/trick_source/web/CivetServer\" | /bin/bash" #TODO: Only rebuild if necessary. + print("....................Running:", build_cmd) + subprocess.run(build_cmd, shell=True) + + print("Directory listing:") + os.listdir(".") + + build_cmd = f"echo \"cd {pathToSim} && {params.get_trick_home()}/bin/trick-CP\" | /bin/bash" #TODO: perform a make clean if webserver code gets updated because trick-CP will no update the sim to include in changes to the webserver. + print("....................Running:", build_cmd) + subprocess.run(build_cmd, shell=True) + + if params.get_start_sim(): + if not os.path.exists(os.path.join(pathToSim, params.get_sim_name())): + raise RuntimeError(f"Sim executable does not exist in {pathToSim}. Build this sim before running this test.") + cmd = f'echo "cd {pathToSim} && ./{params.get_sim_name()} {os.path.join(params.get_input_folder(), params.get_test_input_file())} &" | /bin/bash' + print("....................Running:", cmd) + subprocess.run(cmd, shell=True) + +@pytest.fixture(scope="session", autouse=True) +def close_sim(): + build_sim() + if not is_web_server_started(): + pytest.fail("web server is not started.") + yield + if params.get_start_sim(): + os.system("pgrep S_ | xargs kill -9") + os.remove(os.path.join(params.get_path_to_sim(), params.get_input_folder(), params.get_test_input_file())) diff --git a/share/trick/pymods/trick/makefile b/share/trick/pymods/trick/makefile new file mode 100644 index 00000000..eb612917 --- /dev/null +++ b/share/trick/pymods/trick/makefile @@ -0,0 +1,12 @@ +PYTHON = python + +test: + $(PYTHON) -m pytest tests/civet_server/test_http.py tests/civet_server/test_ws.py # tests/gsetup + +venv: + $(PYTHON) -m pip install virtualenv + $(PYTHON) -m virtualenv venv + venv/bin/python -m pip install -r requirements.txt + +clean: + rm -rf venv \ No newline at end of file diff --git a/share/trick/pymods/trick/pytest.ini b/share/trick/pymods/trick/pytest.ini new file mode 100644 index 00000000..814f6b57 --- /dev/null +++ b/share/trick/pymods/trick/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +markers = + webserver: Tests relies on the webserver \ No newline at end of file diff --git a/share/trick/pymods/trick/requirements.txt b/share/trick/pymods/trick/requirements.txt new file mode 100644 index 00000000..0cfe2244 --- /dev/null +++ b/share/trick/pymods/trick/requirements.txt @@ -0,0 +1,5 @@ +websockets +pytest-asyncio +pytest +requests +psutil diff --git a/share/trick/pymods/trick/run_tests.py b/share/trick/pymods/trick/run_tests.py new file mode 100755 index 00000000..2aaec1c5 --- /dev/null +++ b/share/trick/pymods/trick/run_tests.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import pytest +import sys + +if __name__ == "__main__": + args = list(sys.argv[1:]) + ["tests/civet_server"] + print("Runnig tests with arguments:", args) + sys.exit(pytest.main(args)) \ No newline at end of file diff --git a/share/trick/pymods/trick/tests/civet_server/test_http.py b/share/trick/pymods/trick/tests/civet_server/test_http.py new file mode 100644 index 00000000..af5cb9db --- /dev/null +++ b/share/trick/pymods/trick/tests/civet_server/test_http.py @@ -0,0 +1,107 @@ +from sys import path +import pytest +import requests +from pprint import pprint +import logging +import os +import subprocess +from time import sleep +import shutil +import datetime +import socket + +path.append(os.path.join(os.environ.get("TRICK_HOME", "../../../.."), "trick_source/trick_gsetup")) +from requests.api import get, request + +# TODO: Get rid of this and use automatic discovery when Trick requires Python 2.7 +path.append("../..") +from utils import is_web_server_started, params + +def open_connections(numConnections): + sockets = [] + for _ in range(numConnections): + sockets.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) + sockets[-1].connect(("127.0.0.1", params.get_var_server_port())) + sleep(1) #Wait for the connection to persist. + return sockets + +def close_sockets(sockets): + for s in sockets: + port = s.getsockname()[1] + s.close() + # is_web_server_started(port, "") + sleep(1) + +def get_vs_open_connections(): + endpoint = "api/http/vs_connections" + url = params.get_url(endpoint) + res = requests.get(url, verify=False) + print("Array:",res.json()["variable_server_connections"]) + return res + +@pytest.mark.webserver +class TestWebserverHttp: + def test_404_URL_not_found(self): + url = params.get_url("api/http/invalid") + res = requests.get(url, verify=False) #verify=False says to not verify https for self-signed cert + assert res.status_code == 404, f"Requested URL should not exist. Status code 404 was not returned. Response text:\n#######\n{res.text}\n#######" + + def test_alloc_info(self): + url = params.get_url("api/http/alloc_info") + res = requests.get(url, verify=False) #verify=False says to not verify https for self-signed cert + print(f"Response: {res.text}") + assert res.status_code == 200, "Requested URL does not exist." + data = res.json() + assert len(data["alloc_list"]) == 10, "Expecting default &count to be 10." + assert data["chunk_size"] == 10, "Expecting default &count to be 10." + assert data["chunk_start"] == 0, "expecting default &start to be 0." + assert data["alloc_total"] == 48, "Expecting 48 memory allocations." + + def test_alloc_info_2(self): + start = 2 + count = 12 + endpoint = f"api/http/alloc_info?start={start}&count={count}" + url = params.get_url(endpoint) + res = requests.get(url, verify=False) + assert len(res.json()["alloc_list"]) == count + assert res.json()["chunk_start"] == start + assert res.json()["alloc_total"] == 48, "Expecting 48 memory allocations." + + def test_vs_connections(self): + sockets = open_connections(1) + webResponse=get_vs_open_connections() + close_sockets(sockets) + assert webResponse.json()["variable_server_connections"][0]["connection"]["client_IP_address"] == "127.0.0.1" + assert len(webResponse.json()["variable_server_connections"]) == 1 + + sockets = open_connections(50) + webResponse=get_vs_open_connections() + close_sockets(sockets) + assert webResponse.json()["variable_server_connections"][0]["connection"]["client_IP_address"] == "127.0.0.1" + assert len(webResponse.json()["variable_server_connections"]) == 50, "Should be able to open more than 1 connection." #Todo: determine appropriate number of simultaneous connections to test + + assert len(get_vs_open_connections().json()["variable_server_connections"]) == 0 + + def test_index(self): + url = params.get_url("index.html") + res = requests.get(url) + assert res.status_code == 200, "No index.html file served" + + def test_post(self): + url = params.get_url("api/http/alloc_info") + res = requests.post(url) + assert res.status_code == 405, "Did not receive a method not allowed 405 error" + + def test_delete(self): + url = params.get_url("api/http/alloc_info") + res = requests.delete(url) + assert res.status_code == 405, "Did not receive a method not allowed 405 error" + + def test_http_headers(self): + url = params.get_url("") + res = requests.get(url) + assert res.headers["Content-Type"] == 'text/html' + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/share/trick/pymods/trick/tests/civet_server/test_misc.py b/share/trick/pymods/trick/tests/civet_server/test_misc.py new file mode 100644 index 00000000..ec2dd636 --- /dev/null +++ b/share/trick/pymods/trick/tests/civet_server/test_misc.py @@ -0,0 +1,9 @@ +import websockets +import pytest +import sys + +# sys.path.append("../..") +# from parameters import Params +# from test_ws import ssl_context + + diff --git a/share/trick/pymods/trick/tests/civet_server/test_ws.py b/share/trick/pymods/trick/tests/civet_server/test_ws.py new file mode 100644 index 00000000..fb87809a --- /dev/null +++ b/share/trick/pymods/trick/tests/civet_server/test_ws.py @@ -0,0 +1,100 @@ +import logging +import json +import pytest +import websockets +import asyncio +from time import sleep +import datetime +import sys +import os +import pathlib +import ssl +import platform + +sys.path.append("../..") +from utils import is_web_server_started, params + +@pytest.mark.webserver +class TestWebserverWs: + if params.get_ssl_enable(): + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + # localhost_pem = pathlib.Path(__file__).with_name(params.get_ssl_cert_path()) + localhost_pem = params.get_ssl_cert_path() + ssl_context.load_verify_locations(localhost_pem) + else: + ssl_context = None + + @pytest.fixture(autouse=True, scope="session") + def variable_server_path(self): + return params.get_ws_url("api/ws/VariableServer") + + @pytest.fixture(autouse=True, scope="session") + def time_path(self): + return params.get_ws_url("api/ws/Time") + + @pytest.mark.asyncio + async def test_time(self, time_path): + if params.get_test_time(): + async with websockets.connect(time_path, ssl=TestWebserverWs.ssl_context) as websocket: + await websocket.send("LOCAL") + count = 0 + while count < 2: + message = await websocket.recv() + test_format = "Time: %H:%M Date: %m/%d/%Y\n" #Not checking seconds. + time = datetime.datetime.strftime(datetime.datetime.strptime(message, "Time: %H:%M:%S Date: %m/%d/%Y\n"), test_format) + test_time = datetime.datetime.now().strftime(test_format) + print("Checking:", time, "=", test_time) + assert time == test_time + count += 1 + else: + raise RuntimeError("Parameter test_time is disabled.") + + @pytest.mark.asyncio + async def test_variable_server_vars(self, variable_server_path): + msg1 = '{"cmd":"var_add","var_name":"dyn.cannon.pos[0]"}' + msg2 = '{ "cmd" : "var_send" }' + async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket: + await websocket.send(msg1) + await websocket.send(msg2) + message = await websocket.recv() + vars = json.loads(message) + assert vars["msg_type"] == "values" + assert "time" in vars + assert len(vars["values"]) == 1 + + @pytest.mark.asyncio + async def test_variable_server_sie(self, variable_server_path): + async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket: + await websocket.send('{ "cmd" : "sie" }') + response = await websocket.recv() + assert response == '{ "msg_type": "sie", "data": ' + + @pytest.mark.asyncio + async def test_variable_server_units(self, variable_server_path): + msg1 = '{"cmd":"var_add","var_name":"dyn.cannon.pos[0]"}' + msg2 = '{ "cmd" : "units", "var_name" : "dyn.cannon.pos[0]" }' + async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket: + await websocket.send(msg1) + await websocket.send(msg2) + response = await websocket.recv() + assert response == '{ "msg_type": "units", "var_name": "dyn.cannon.pos[0]", "data": "m"}' + + @pytest.mark.asyncio + async def test_variable_server_shell_access(self, variable_server_path): + async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket: + file_to_create = "tmp_a.txt" + await websocket.send('{ "cmd" : "python", "pycode" : "print \'Hello World!---------------------\'" }') + await websocket.send('{ "cmd" : "python", "pycode" : "import os" }') + await websocket.send('{ "cmd" : "python", "pycode" : "os.system(\'touch ' + file_to_create + '\')" }') + path = os.path.join(params.get_path_to_sim(), file_to_create) + if os.path.exists(path): + os.remove(path) + warning = "This test proves that we have shell access through the websocket api. Is this a security concern? Should this test fail if shell access is available?" + print(warning) + assert 1 + # raise RuntimeError(warning) + else: + assert 0 + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/share/trick/pymods/trick/utils.py b/share/trick/pymods/trick/utils.py new file mode 100644 index 00000000..a18463cf --- /dev/null +++ b/share/trick/pymods/trick/utils.py @@ -0,0 +1,100 @@ +from time import sleep +import subprocess +import os +import psutil + +def pause(my_str = "no message."): + print("Type exit to continue:" + my_str) + # os.system("/bin/bash") + +#This file contains variables for the civet_server tests +class Params: + #Change the following to change the default parameters + def __init__(self) -> None: + self.__port = 9000 + self.__var_server_port = 9001 + self.__host = "localhost" + self.__enable_ssl = False + self.__test_time = True + self.__ssl_cert_path = os.path.join(os.environ["TRICK_HOME"], "trick_sims", "Cannon", "SIM_cannon_webserver", "server.pem") + self.__build_sim = True + self.__start_sim = True + self.__trick_home = os.environ["TRICK_HOME"] + if not self.__trick_home: + print("ERROR:", "TRICK_HOME not found in environment variables.") + self.__input_folder = "RUN_test" + self.__test_input_file = f"tmp_input_for_test.py" + + def get_sim_name(self): + sim_name = None + for file in os.listdir(self.get_path_to_sim()): + if file.startswith("S_main"): + sim_name = file + if sim_name == None: + raise RuntimeError(f"Did not find sim executable. Please make sure the sim in {self.get_path_to_sim()} is compiled.") + return sim_name + def get_trick_home(self): + return self.__trick_home + def get_path_to_sim(self): + return os.path.join(self.get_trick_home(), "trick_sims", "Cannon", "SIM_cannon_webserver") + def get_input_folder(self): + return self.__input_folder + def get_test_input_file(self): + return self.__test_input_file + + def get_start_sim(self): + return self.__start_sim + + def get_build_sim(self): + return self.__build_sim + + def get_ssl_cert_path(self): + return self.__ssl_cert_path + + def get_port(self): + return self.__port + + def get_host(self): + if self.get_ssl_enable(): + return self.__host + ".ssl" + else: + return self.__host + + def get_ssl_enable(self): + return self.__enable_ssl + + def get_var_server_port(self): + return self.__var_server_port + + def get_test_time(self): + return self.__test_time + + def get_url(self, endpoint): + server_port = self.get_port() + server_host = self.get_host() + ssl_enable = self.get_ssl_enable() + base_url = f"http{ 's' if ssl_enable else '' }://{server_host}:{server_port}" + return f"{base_url}/{endpoint}" + + def get_ws_url(self, endpoint): + return f"ws{ 's' if self.get_ssl_enable() else '' }://{self.get_host()}:{self.get_port()}/{endpoint}" + +params = Params() + +def is_web_server_started(port=params.get_port(), status_method="LISTEN"): + isConnectionOpen = False + try: + for _ in range(20): #Wait up to 2 seconds i.e 20 * .1 seconds, must wait for service to get to listening state. + for connection in psutil.net_connections(): + local_address = connection.laddr + if len(local_address) > 1 and local_address[1] == port and connection.status == status_method: + isConnectionOpen = True + break + if isConnectionOpen: + break + sleep(.1) #We sleep to use less recourses + except psutil.AccessDenied as e: + print("psutil.net_connections() requires root access on mac. Sleeping for 2 seconds instead.") + isConnectionOpen = True + sleep(2) + return isConnectionOpen diff --git a/share/trick/sim_objects/CivetServer.sm b/share/trick/sim_objects/CivetServer.sm new file mode 100644 index 00000000..48892acd --- /dev/null +++ b/share/trick/sim_objects/CivetServer.sm @@ -0,0 +1,20 @@ +/************************TRICK HEADER************************* +PURPOSE: (Trick HTTP Server) +*************************************************************/ +##include "trick/MyCivetServer.hh" + +class MyCivetServerSimObject : public Trick::SimObject { + + public: + MyCivetServer server ; + + MyCivetServerSimObject() { + ("default_data") server.default_data() ; + ("initialization") server.init() ; + ("freeze") server.http_top_of_frame() ; + ("top_of_frame") server.http_top_of_frame() ; + ("shutdown") server.shutdown() ; + } +}; + +MyCivetServerSimObject web; diff --git a/share/trick/sim_objects/WebServer.sm b/share/trick/sim_objects/WebServer.sm deleted file mode 100644 index 66774b5f..00000000 --- a/share/trick/sim_objects/WebServer.sm +++ /dev/null @@ -1,20 +0,0 @@ -/************************TRICK HEADER************************* -PURPOSE: (Trick HTTP Server) -*************************************************************/ -##include "trick/WebServer.hh" - -class WebServerSimObject : public Trick::SimObject { - - public: - WebServer server ; - - WebServerSimObject() { - ("default_data") server.http_default_data() ; - ("initialization") server.http_init() ; - ("freeze") server.http_top_of_frame() ; - ("top_of_frame") server.http_top_of_frame() ; - ("shutdown") server.http_shutdown() ; - } -}; - -WebServerSimObject web; diff --git a/trick_sims/Cannon/SIM_cannon_numeric/S_define b/trick_sims/Cannon/SIM_cannon_numeric/S_define index 48b554c0..ef7d6480 100644 --- a/trick_sims/Cannon/SIM_cannon_numeric/S_define +++ b/trick_sims/Cannon/SIM_cannon_numeric/S_define @@ -9,7 +9,7 @@ LIBRARY DEPENDENCIES: *************************************************************/ #include "sim_objects/default_trick_sys.sm" -// #include "sim_objects/WebServer.sm" +//#include "sim_objects/CivetServer.sm" ##include "cannon/gravity/include/cannon_numeric.h" class CannonSimObject : public Trick::SimObject { diff --git a/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/cannon.dr b/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/cannon.dr new file mode 100644 index 00000000..8810bedd --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/cannon.dr @@ -0,0 +1,17 @@ +global DR_GROUP_ID +global drg +try: + if DR_GROUP_ID >= 0: + DR_GROUP_ID += 1 +except NameError: + DR_GROUP_ID = 0 + drg = [] + +drg.append(trick.DRAscii("cannon")) +drg[DR_GROUP_ID].set_freq(trick.DR_Always) +drg[DR_GROUP_ID].set_cycle(0.01) +drg[DR_GROUP_ID].set_single_prec_only(False) +drg[DR_GROUP_ID].add_variable("dyn.cannon.pos[0]") +drg[DR_GROUP_ID].add_variable("dyn.cannon.pos[1]") +trick.add_data_record_group(drg[DR_GROUP_ID], trick.DR_Buffer) +drg[DR_GROUP_ID].enable() diff --git a/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/realtime.py b/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/realtime.py new file mode 100644 index 00000000..9998f5b6 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/Modified_data/realtime.py @@ -0,0 +1,11 @@ + +trick.frame_log_on() +trick.real_time_enable() +trick.exec_set_software_frame(0.1) +trick.itimer_enable() + +#trick.exec_set_enable_freeze(True) +trick.exec_set_freeze_command(True) + +#simControlPanel = trick.SimControlPanel() +#trick.add_external_application(simControlPanel) diff --git a/trick_sims/Cannon/SIM_cannon_webserver/README.md b/trick_sims/Cannon/SIM_cannon_webserver/README.md new file mode 100644 index 00000000..a55da54e --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/README.md @@ -0,0 +1,31 @@ +# SIM\_cannon\_numeric + +--- + +This is the second in a series example cannon ball simulations that one builds in the Trick Tutorial (Sections 9, 10). It's purpose is to introduce Trick supported numerical methods. + +Rather than using the analytic solutions of SIM\_cannon\_analytic, this simulation uses Trick's numerical integration, and root-finding methods to: + +* Determine the cannon ball state (velocity, position) at each time step, and +* Determine when and where the cannon ball impacts the ground. + +The simulation is otherwise the same as SIM\_cannon\_analytic. + +![CannonPicture](images/CannonInit.png) + +## Parameterization +Same as in SIM\_cannon\_analytic. + +## Initialization +Same as in SIM\_cannon\_analytic. + +## State Propagation + +This Trick simulation calculates the cannonball state by numerical integration. +Acceleration is calculated in the "derivative" job **cannon\_deriv**. It is then +used in "integration" job **cannon\_integ** to calculate the cannonball's velocity and +position, using the Trick **integrate** function. + +## When and Where the Ball Impacts the Ground +The time and position of impact is determined by the "dynamic\_event" event job +**cannon\_impact**, using the Trick **regula_falsi** function. \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py b/trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py new file mode 100644 index 00000000..4cfafb49 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py @@ -0,0 +1,27 @@ +exec(open("Modified_data/realtime.py").read()) + +#========================================== +# Start the Cannonball Graphics Client +#========================================== +web.server.enable = True +# web.server.debug = True +# web.server.port = 8888 +# web.server.document_root = "www" + +trick.var_server_set_port(5001); +varServerPort = trick.var_server_get_port(); +CannonDisplay_path = "../models/graphics/dist/CannonDisplay.jar" + +print("pos: ", dyn.cannon.pos[0]) + +if (os.path.isfile(CannonDisplay_path)) : + CannonDisplay_cmd = "java -jar " \ + + CannonDisplay_path \ + + " " + str(varServerPort) + " &" ; + print(CannonDisplay_cmd) + os.system( CannonDisplay_cmd); +else : + print('==================================================================================') + print('CannonDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".') + print('==================================================================================') + diff --git a/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py b/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py new file mode 100644 index 00000000..3af8c1d8 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py @@ -0,0 +1,11 @@ +web.server.enable = True +web.server.debug = True +web.server.ssl_enable = False +web.server.path_to_ssl_cert = "server.pem" +web.server.error_log_file = "log.error" + +web.server.port = 8888 +trick.var_server_set_port(9001) + +exec(open("Modified_data/realtime.py").read()) +exec(open("Modified_data/cannon.dr").read()) diff --git a/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/unit_test.py b/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/unit_test.py new file mode 100644 index 00000000..9db27413 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/RUN_test/unit_test.py @@ -0,0 +1,3 @@ + +dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 5) +trick.exec_set_terminate_time(5.2) diff --git a/trick_sims/Cannon/SIM_cannon_webserver/S_define b/trick_sims/Cannon/SIM_cannon_webserver/S_define new file mode 100644 index 00000000..4656454f --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/S_define @@ -0,0 +1,40 @@ +/***********************TRICK HEADER************************* +PURPOSE: + (Cannon Numeric) +LIBRARY DEPENDENCIES: + ( + (cannon/gravity/src/cannon_init.c) + (cannon/gravity/src/cannon_numeric.c) + (httpMethods/handle_HTTP_GET_hello.c) + (httpMethods/TimeSession.cpp) + ) +*************************************************************/ + +#include "sim_objects/default_trick_sys.sm" +#include "sim_objects/CivetServer.sm" +##include "cannon/gravity/include/cannon_numeric.h" +##include "httpMethods/handle_HTTP_GET_hello.h" +##include "httpMethods/TimeSession.hh" + +class CannonSimObject : public Trick::SimObject { + + public: + CANNON cannon ; + int foo; + CannonSimObject() { + ("default_data") cannon_default_data( &cannon ) ; + ("initialization") cannon_init( &cannon ) ; + ("derivative") cannon_deriv( &cannon ) ; + ("integration") trick_ret = cannon_integ( &cannon ) ; + ("dynamic_event") cannon_impact( &cannon) ; + } +} ; +CannonSimObject dyn ; + +IntegLoop dyn_integloop (0.01) dyn; + +void create_connections() { + dyn_integloop.getIntegrator(Runge_Kutta_4, 5); + web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); + web.server.installWebSocketSessionMaker( "Time", &makeTimeSession ); +} \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk b/trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk new file mode 100644 index 00000000..26b2544b --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk @@ -0,0 +1,3 @@ +TRICK_CFLAGS += -I../models +TRICK_CXXFLAGS += -I../models + diff --git a/trick_sims/Cannon/SIM_cannon_webserver/images/CannonInit.png b/trick_sims/Cannon/SIM_cannon_webserver/images/CannonInit.png new file mode 100644 index 0000000000000000000000000000000000000000..8801beede934129288223571d804717ec5f45f25 GIT binary patch literal 46380 zcmd42hgXx$^FE9sAfkYP^d=qYy%VK~bOZtE0@8c$L8($CQluA=CL*B|AVBCvdM}~( z-bsKE^2X=;`TY;?$;sK|zIXTR?#x^>bM0=7j+V-kN3@S{aB!ZezERS}!MRt0{gOV! z$3CH8hU#OBdtSOK3OL|l`d#cFd@FSoB^=DZ&ySXpWE`ANII2po-ucfTc4l3#Ozv+A{Fv?e7QVyu{D$-iZRIw*f&iKYT4L|3S);lYlZ)H5>3X5 zt*GNI^Z(l}VsWE4$oK^xSPfF!4rlidW{A*;7&Aw#G{7qC<+uiux9#TtdoE;6s=&O><|X&sfj}y8p3jD5TD1P`~+XPWdt4(aijc-P!l!)8$T! z)r$cjQQPW_+#nOok9P^o2vfi|j7ZV-y=10{U2LDbWxM(0B`mOzR>OiYeC>>~o8IDl ziaY$d%1^gur-Z`i^2f7B-LClyfw8#6<(Pii)5B@1U6lVydFEll^+_V&o;y5GK)0h% zhyD{*AoQR7ueV5!okM-LmzSBxJ^VZgk(fZn_?Be#gudgw_7@bfaxY_&*FLcY{Vg>; zb`3f0VDc{;cUU$=kBQvEqsor2>Z>y;VsR;+dFj$u{t4K59A$B9fs=qbw=zeMQU6E` z&CVVvs=%bwCCbqJ)CE$+3KZt&f39l(z53icK`tD2xQxn*4t^q4~V$;km z!Lk*uB@diqZKJG)r+-ep(p;bS7@Lq zKNS#`r^D4%sG6KE=0w^4!5s$D#tt2)XD!r_csl~%inCH5pihrp>ep!H?^C&S6XJuE z^_J*(177g(G_FR`y>IknX9PvRWd1a;*>UI#af6`UsPX6K<_5E+lKYc6k7qnyV3%yP zD|J!P{Q@Li_oM+xOy`z0Hl?VCRp&TLzO}Of0nQw)bzdmFyC2(wiH0i#x)|mOUAzjG z$BuBl7qq=o0DAqf%A)5t84Cr0=;ybbUi<08E)tmKS}@P$o?mR27uD@0&xk7^&B9dynOuijpV$l9oMo_5ZE%I+I zDfx^iOEuNz_@!meCJ(vRSc^8oN#s9-Duw@(NwBAi$QVvDVDomj+f0rYYu=~uHlphN z-*FI6(SlgWlau<;ELwYS4d_pJfomkqoxEGwQd?zy#X1*)TSHk8Gq^$7ubRzCp?g+v zG#R1r`O@CrUWmLayH+f2R$nkMWOM)V)iu5CuK>+Q-M-FC%>mH*gwx`L11>^r16?(i z#6B(5tO*w+cMYc?wNao|u^jKiX%B&g z5zD{+WuI}i(l3F1B;DOXiD`W%e*)~d*UH~vkT{*8h3wx|J?Sv z!AR}er=QDJu$8)A+sY<+g}YCs^yu<_ic;;WpG0GdidU=Wg!ra_q&Y=}g*yZ#IBO14 z7CklmRE54uk*B0!v$p(A$lEN^F!_+1gL99vUSp4!`O#7C2aSr7^<@Mal(+uVbun-|_(T`rke}EKxE?Y7M)v)nW1Z87m<6y2_){J>jxgIKk zw?w>^cg)gmdUi>v@e&K|YwCpOM`(aT!x>HJA^AI&hHU~!4fEV-I`8n^gw^oOYEsNh zm1twh_B`r#&k&|CQIBa_&UhG-X#a#!HvQ+%5LkT4e=gxlfp@7vJ}U!IAK<1(?%7|n z5P~e=#VBSk%Rx%d^OuBfkG|494`E6}Zx-+~`21CSOIKzWa{gKuPkh@vn6Wav1MIn7 zx(OflJM)*N2VSkBY;GO+LB3rM<8iTBPp3zXCL~om+1Cqa z1zGPaKitdL`8AP5V^N!{2S20v{PGjIwrt`Z8g-loui)iW2Cb&SZrLT4gS`w%6vq^~ zP&?1f8O75zD@>nohNN*rW&5FDCV(E#(?KVLDyY_^j#mMS9uD~Gv|H6e)e$|DsahQm z+)=g5g4G`+Nn}rOdg;9eeM6kn%4cXnj!sB_*=+X?LX>7Y7QBB(pKN8h3Hu$HE(PwF zGo(7bDABceJ9k7;=iBb{x?jdDTER0M)x%V%b`2{fIFoR}1dcnKW&B-=x}0I&Hj zUR*snn`4POv-^aw$jvD||kz9zbhzCr0xX8&5ijXaqmB0UJ{h zr&zoD$tmbEU+eq!W=jIr&r{JnKBb5S(1=DmXYo~npV^|Acf^ecPom6E1xHMUaGf>@ zZc%O!FC-D1LeIynO+}1EgpcY+YETbiZS0uWb_s-V8S2HGifdd$(_K}1qDGqeZ-asD zWKrA1bFLP$jN9K0%`NYGnQAx zS0ht5ln2O*A?G}@1U>40?RxT<<-|i|25HvC-K}!kJaPRck#o6C(=caf14!()0K`wb z3wZ(v@*#rBfCWgmnAWSWXE28{9=~Jt&(BF*Hxs`jk1o<>NMLDsTDNypev=E2^*m!7 z%(MUK%6b{D`1+y4a{iW=NUmH@RRHVE;CzsU@u^N-Z5WQtKK|tEmHCykj3~0TQb6 z^dIKkCbpB%FQ}$JUfuq()C+;-&hO9q`YnM;-IF2)-$O__bduel5l9L&pWG*+X$n%ubZV{2H>27Fjm>{;<*o;Y?5-NM(Wy&#O!Ap7N7lhK5)%TG(TO090@1+u*qhGU|il+#M=Zz``@z2&t*L#;hFje;|h^+Ubwvo%$aFq<8z z>*A2vgxh+(14M||?*s@N2_paJEK;e={XcIGq#e6&$+Xps$PaAig|=q3ya;4T+~d$f zc|Q{{^W$Q3-JHQJUa%kcS&dSxbaP%8x;mAaK|T@W&TWa<=(~^fM?XdFTeVI>4{K)I z?09U2E$vr$zCwkKyOk?2UsSad)(d_c!r?^CxQ)lZqTk;hA~WVH&I@PB37C5I$@!1R zQ7sZWgim7`JM|6ClvPy;4NX1p64qBFwNOe5k@r6pOAWq|jP<(^$YX||3=43l`w-d5 z*G84_(`P!=8n|a1f9MIW%uSBf3r5U$X8X^%tp<&d-NCsdjFQFf#^H$!|5RWv%civG|n9FLVCV$c1xIRViYz( z*b%zD>II~R-mWu21F2(=)cn5&-$DScm~o{nb_+0i4KfJ}Eqk!>V}JRZtx16qDUqVPV4Spy3jz$wK|!H1O+*6lA9dMy?G5v zr7r1{;5lGG6j*7+GKQ~9%6tWIw{9uPucc&LE*OfX(B*}GMpeiVWJ5AS*1as!jdMf0 z>Y9Vj--#~(A48fajAQ$_tZq}@QbZxN#+qa8*{NFZ6gdE5xuCLoey6u941H)3Cd)C% z^7=1ew9h5Of{j5df=!)WaG#MsGiiD0NPfrp?qa_*g9=Ra{;Z#@@S1mdr)AyFKx+M& z96nZ=8Im{piHNNyRfZO1i+sKok%N@}_=l+<1HQvB%zu$8hrAx`D}8hp+ZjEl+2Ump z*#VcPbE`zivTv1Sx2rX%2J{46y8F9n;3uZ9t(g{@=-s6_oL+0!VF_%QSkb^>$;buZEd&aaITnZ@p@6`L22%u$WK+T*{@x$?PI*eO;J)w>kz2mbSnL+?*}SU*TiRBJ-Tue{BhtVo-^ z#3W$v@7PWkY&=_fw|(8R^}O{e*4MR;&nI#v6THl2zMswhMIeN(RZgb+nND{G zRk(aF6(2UNdPp8BwBI~23nB0WwUV2Z<;u8;pl;zt#&)=>lmMU{=dA}Wb&23M3Fn!O z)?4gY7{jZxTaVs5Uv20!2JSq+J70r3JLbq`0X~pn0poil{BLr5rBFu9Z)C3L)3lsW zHam1kKUHq~L=X$4r6&70e!k^e{n?lrzyBeDMu6k}b4PPGH-WP!Yj*{Vo8fxff|D#B7D+H=_I=G*KS`o{jI$mf?XTg0Q94~W>)PP+ed4JuKh&+Pb zg5F%R@dQ3r%WfTZ-WWFG$G5m4F>gRw4a+)k;GPR?taEVXND_Tw(tsx5<@}Y4xb|Pd z0mIfF{>Z(@iB1ePZ}J;3WB>C2GKj)e#D-g;6*o>nP<4`%2@i?WYbkXpr?!AugYA*> zspsmf?5lrFR$`r)X%F{M%{=~6x9~Lg7CJ$#)!l%y_YhZxy445Ai=krVzOT|>vMX@~ zl43^%W(zgr-~@$uN@aezK!6wTE10jTp^H2l*geOjLPg96UM6%~@~j)57?US_w7M-!a-L>Y*U zs%+G}Lr#Q)^XlUz`CV2#6(Cs4jyKihY_d5vIR?8eK5R#Tc!lo|Y{= z9EPWeNyiIsdoISPc>1-w@cz;i&L5q)8D?okxg}dy7`4TI-7E}4r~B@Fj71E;W5@Z+ z`rQ<{S}xnP*Q#}>-1=%k_|kTmCYV2g&plh<*vT$HPTBS9bl3fi(98?t7c-i_5Uj#| zY-*AiaLBv5=m!BDgrfvDvlG{^u6kw6{WP&5WABfyQae5LuuIC|4IXC*O3B>+LK$bc zIaCOfn~T?0S|m+fa#G<}&kWju-^oK=+4Hi(sTbjju(x`<5{}0SJgyuK=QTl%)^{#! zhg%5WG{%)!A;7ksbww8+t|S7CEIr=Hy6!n{L$=mfxFQ+#>Q1YL@9Kg^OG^V1 z5iq7}YA>zeXu<$JKW^}a^2SD64y5zP=atJ2?O}M+F_J{_541Q1YFyM4n85BdAtT>o zJg|;L6MTvzmh&&EV#iWP|LlQbR%r&%d)=wEd7WT!unkaHXXSjl_hcdDF&Juz@X@Xm zx~0Oe#q_&WIj(XPFJC?V&N*UzSN%kk)aoL|L4(?~(y)h8g8UZPF1Jv%;;YNB;O8pW z^~@T)xhG_})&shY{aGubCH?g7)SJV^O_3tkpO*g`7UBi22n#x)0Q+?wIy8JdyQFWv z6E8+A?KRb88Mq_63A|8bD?!?4+Y`xW^>Yw8(i<$Ud7ebwNAf|lsH4*k%t{he1aAH4!_okm*)kD- zYfJO&2S@Zkw%oxL0w8Q_8Ys6+dtlR9&Dnq%({M!3!9rqS7GAeh{9V892H#;9chh?% z(HKJ?O~SiL09hC=qXMX|EgOnB-Et8FPCb{vz_esB{~MI{iqt+;@CXj2#ZWELk>&1I zAd9BYVCXHaXYo>>nHLN)6S=$o7f&N#jk*I~-}s7-Gp!N0%&n%HHwdA5JnS0Pg(xp$Ed)@(RotYei*$wPF6K_^uLtIdu#< zL9td#Gw31KNOt`GI?6i&<4(VhoRz;SIn`2`$qX2toHm6G^yrzHDz&te^w69UP?w!^dhjRq&UZ5je0;X0Hj09_q+>XxGGKw_D*-c8B zMR1?gelE!&)QRX~@%#Zu?iD(t6zf<9c5#^J`l_|Js;h@N4yC`~J@d&>!v8Q3m3c@k zz~H5XsxQHmHZPhEb5_WYSd+;2ox2GXa@6}*5b6!adrkDh>~<8Ry~#KrjBE3!kgAJ6 z-PuAv_9F;aE|Q-^#a!+-^(#N_y8610zOgr9b?j6*nN^gLKLWwvFEk=qe-wU%S(N<` z6BjcCD=^nG4=v;iO0?`Y7%JXM0x8J@soV!gj@E-2a$7fyv*I$0!K z@BVJAYppHB@vjwTtRvJS3Aouo;41+|rtG!~Kx469N2h~gYiCl8f%{_m{^`R|!)2wj zvlY0CYY2K|kkonY=B%*$d~`dRhO&h*KIX? zIY$p!c_SXhSJ?P@*(Er!+vVJ<#k%2euB<&U!7KFKyad_efn>!^EI*n909gfL6X}bv3U7HK_C^B1Z7J;~8P;$TK7jGkv&579yMBrmD8jzqI zvGRFmKIY zklv&mruU{?yv{m>0zPl2+10zrQGpvBC1m{1#jaK*MBw-Bp$6LGg~s*u04V)y#s&77 zU;ntDSB;lVoLUV}v)N_OcIZ?2g0o=Do_N-xQ~}D$AFs7G%l}g7bZuhph;U#nS4zd& zfD90U++Ys~0T%2Yl!V>v_&pBtf5&O*x&Is}ze=2%-Iy+d@9SoHzhNc59FFDh*G?Dd zPsV!NH}pi{49l5EmCWl7hJG1>pUF6tNfcuAkV^Z243pkA3x4_7G4j@SB=DLuHP$Hj zc$~Z~FoBL~;7M=&t2yzL9fSo$&7C-PmcT* zS!!-$QXc3|%DjAkT@Bi?8r{CJHi+nvltp$h7JkQ^Ar7DY(EJs~?tbZG7Wnml8HJDG zje955DPXmoAm9K{QT4%$oe;%$=ZlF8b{82eb99s&9w>#hcG=(a%MB#iM~u$_{#>Q> zYts+mxn-;B2WtkQB7SBxd#wfENuil2g4Cfm9EX$&ZOSDsA!sD0@cm|!#|*^VOO@*( zfL1)u^QH^h|K|{UHi%cd;)b`fdV%4D;p+AxU#vFWdkXVSuwFMe`d9{+402Y0N((O1 zb0tjkLhz({UWFD{{y-ce5M&s^JC8}dW;v8g&$jmMBfA}A+fecw2yGyfVMjI+(H3~q zFGf25I-Ni(+|6QgDy^hTb<9g=ZE~nID6Tn-sY*17WvVs?gFZ5$NA2b;cnyEjivHSi zf{%?}oR(qV2EdYu`Dt?9N4)+|Mb;OtR^6y?PTu4ipMwy-pxn-pUQATC9q(5BJ-egV zpha>R$uMSXdIlt0N09T;rX2)aY&=YZ!LJ>oQBVJu95hKq_SWnmiKvq|yR#&|_3EYR z$YV+1UyKcchb&g$s;#NjGp*Phy`rl2*!m;y3(*)fq zHhaE_C5%1_zJHh*&A;8+D)2U0I`^m9ZPZQWnjY|M$tTzVrB#B6QLedKoLm-`4^=R< zxRK8DxXIQ;GlU?x zHPRQR(k2qBv0dTYNQH!aDYm|~z012scgRRL9?J$28-8s}$fG|~Y`*!NNO`9(<%my# zrg5AQv=jOBo{&}L;FZeaoAj66*!BixiFh`Ah5lc2-k-bF62bm-olNo(m_KO4AgId7 z>dh(4Zp!1Ii80Lsa`=BBcsSOse+iwK*YmRLUC8#|D~vcdmy)4e5U z=efa=t-DOSX}Q_bmPtUE3CD7KDX#Y3-gCr5;w*gW54aT-pHeCNhL85%9{xX8#z&iF zmun`(ov4#Zsv%i0;F2CFbjMGbU9UqpVa_H%mEbxwA15P-3u#p#5x`<^mil@ zV~i2hSWWG8qr9#5GIcm1F$%tU0cP}Kmlv~esPNjDEQh->{Q%4T4?}!&K33o7E)SW; z1lceN-Hov}ZXCMlWzCK*$v5suF3DL6g=XDR#iCo9Zp1I%C+z4vM0^kU04Fdnm-{cY zcZM6RU9i8mWv%nkCI#`b${_6@JHA4#(?p|Y~d3n zeOG&cptF7P^inm)EOoeY93DAIrOhywiVA$JtH!P+(=N<9In$!RCY*Oxt!B&;_6j$z zeVgf(pg*a_R16tb_J_&P2B*{G`mBnJ8X<|(a7BJShgDRDk=}hK2V3moGpvh2pa1ovSjXo!mp-oB= z*j2%?@MFQxtSeq|6;!@$XK|KRD_1D%sH2qz+^rN05EvoolMCaW5 z)C8NkSzj`^@0VUrp=tb`31q4Jd{_}mN-N~ii(1#F>rW`%| zoY5)Bt9$+}?g!NVRQ_cV4X9Ip0T}vlEzck7k{^P^!9bkM6&&b z=|m2@MKyVOVr+~g6T%%bY4%H-CCUtkpG)|WMJjU^ILLsA4*wA6Y74CFjY*t^zg^bz zD01zIcQEPR<-?u}QJy*Rsmo?b9I- zpRNCDGpEE7)~)`iZz$-Cj=khvn|-~{vDEl#{z%?N6N%dbKiGR|CGTV)W@cgl>v@w>Pc!A`pe`1wax!^H)VX>yaK2Cc*r zCZfd|uFrnPcYidZ-1TlU`{8U-?W@S&*f_x5hau=xxg$0ssm-(A_9$9!wBB=xH^k0I zvw|hv^l_en-HY#`ca0ww?p2>*H=2||?H<_`k&<%;Df5|_v4soyEb-`4@RM;e8Z8#a zu6njq8!IDQC2e4a6>C)uP^QuU{wlLGOW{|BUZ?L+xGUhB?T_(q#-eVH>VT#wejsW8V>9kD{{I=rE?Q z2It?2MSB<@H|NZmS3YQ+n8KY`{-<#dHMW^|0M@x`X{-Wu%|*Zblm+CQ8iE3aZD=&dqY z^vYRRuXjmSE0QKOfD5^#rq~!w+CeHEtnf+~inX@$bVak4HCn?xA`1{Fk0=&DzrxK2 zQT{C(Y)aa@S?D6BiGryRXPF`Uz*?=eCq;;>hm<*GhDOqrd}$GQEWk(0L9wS;5! z_a~>PPaDLE{)%d@-BFlu%#4m#{GPZ*@A86aq`#||EP<9S=BFTvr_Y|=FJ^pzG0P5U z@sGjoS6jix^mt1?R)-b$NTsB;Xxi9_Y;FcbuwECA%WF(@h^`+}YWH?Uxg;~qKloMf zp$@Vu{;YtuRLb7kR=-JnoH3A(Vffo$W~0~SUlp;KhGJCsMLqF(_$nL{J7K5360r^q zRI*6;r(i2DA=y@^nxd01E$cGFz#`W{U$Z|44p=yz%?HqFU(wdFRG910BO1@#-z z2y5rKo_wLdJ*vq3{=afze^%c67I{YeM2!m_ ziH15Df^290ZNNOG|3&yT^_lfRDuKhF(5%b-iOEUIiP?8*FN!QpC=(VOPY*P{%`27h zwNCnKNawhYSUK~5(GzbBd*`ZNa*q0aGlr~@zoL_QucA@;{X$$TS(LXMJaI3u>A#nXP>Km*?zZwAUMs#li{V z1Z%6zU9Ho?gj5|BL$lsI@z0K|tJ+mI3Ji(Bshr`8==0t4IcCp+Fd2sRzu)gs3S=u* znCqSTBaWK1W6|o&IMJ8kNXSdIj&Yl?QoW|YcNXzCwOBPLh&36h{;9+=FxPNbkz)8x zx4uZ?i6$N!8GiBZiX^v2jn@nR-od5>*OmmzcP1wAmP#|?sc2C>5i7mz2Hn5f6`P8? zb;49bbB*avv`Vb}TO-<|&Y;J6592*Ih&+oO?f=-|;mgAY66(n{=ml$j;}Vdyv2uRpQ620&8Hp?kb~ zR>m2+iZmXc(ncB#ivbLHWkX>}y7fe4FNHtqiUUy%Nn^Tib~`?Aq~iJd?Htgr{1vl|na`>PhhGUFdbG}T@R_^w~p(WNL4MbH~RvJ!i5@_hqz zHr_dSyKctZW~oG(*GN1n(^w!zDbt@tOE`m>GA{8ATTJhImzB`kspOb_iW18!NqP>l z7}6M}FWpvb$^E|jzctgH(-h~fUK?ar{;3fxQb^&Y9_ULWzEkUX@J)>npX808y-rwj z^HZ}BC)oX`xh&Bzi`?d<+`sBnxogQSNe`~zybasI^oT82kPd6hNQUW zIz=uc<4-)-&zM{YQzIObJ2i5wgdF~MQy}Hb)Q^u1S-T*vz#J0LHC=QLQik~KAg4ke zx~HinfA`vU3C&3L*z4Y9^VN3fec&((j++grqL7S4ncic(2C3D{>O{BOBnH0JZ4o(62aPCrJfOZ^m-yr>`SsNgl{U zYkcH#Q;RPeD7k9*?`)3B=(E&hn>rm6ximf-kX~z1V%>hVgYz&)qF^O2!GLhpVY$N+ zLmb#w-`(ythesuo68O`72 zph8$6)nP5Kf2Py&$XYL^B?4IPGB#fq%>P*tWNB}|Up2ln@^-uKWVAD2GFtBw9d*|x ziABG~2sD*|GP_%wGs-uV6x!c*&jd2#?FBJMx{vcvSR|skwQQv|)CXAPO-@AQGyG(j zhT_iq!&=|H+a6mu`1Ypo4I3F7nTfS(j8fP~VO5qydyr7w;r@FjHxtXhUEgVaNe%xN zf4qDDx>?k!;$iaor(*S+%Q%nWxg;7DhJjx~q0Br^lk()nQP12w_H8(m($ciGRv)*b z;#A&GEi{Sf_Vl{J+ALfpLD|Oul@PR*s~o`VV0A)!tC&;%c~$2fF}r|3UUPGBac(G` z8+cYpUtg+h#+k)Ar2Rvt#Mxk8==j}|qV^s42*1N^n1qu5Wgei-V`wWy)gvmpTP3>KV9|a=t!tq@f&8s{B*(W?d{&9I9Lcb)h zr(>N3_ww1}{pleabQJH4GGZ1+zdEfSnts(0^MCT|{dIfqmlr$KgMw7vJTG#pelKEw%>SnMhaO*(((WW)0)~? zj^u=_Yf@lny4QK9L0XOb$_AJ_()wm^0u{^&|8U?ncre6wXe)dOB=~{ATJ?KNztQ@z z)$l6p!|*m8R|4-AcDt*KT}4jmAY6EuX?OWZj-@dnEwBHx8{^0dF~0V|md0KKVuL+J zKA=BAKz?c!Nl)8z(fPCaVs_$B6WYHgV6NnXJMtUxQ@T`f1>Q%WR}7E+EPnh8#AavK zC|r%S^vGRTM9KsFV*sJYIyl86 zv?R*w0V|D$?=RtV@pQhALW69nFXXdJc)tQ#pfTsYAA7vdHu6NS*FpvTG-*ajer-2e zU}!nrSz%2(f{#9Tc)6`rRNSQ=!?JrxFg0JwNO;%>h{g_Ij}Phrq!Xm(W14SA_2Y-` zso9=nNw{t27r1U9NBFmH6Qf;2?~PHi=$?EvVx1eS{kZ+5ron&Ipy(9Ej$B0c5hn~< z?n!7zl1S*yY_!R1#gF<>o84zhwKK0+o^9lE_EY!=5|tHQrB#+sFMYIYd=Iys7br@a ze|{-0UQKtPn}~ITC@J?!`!iUpGxRo!BpH=^6t%*8zQ=N-{{W;mfe2bMdTweVmcc3& zD7D-c?+pS#S5YF7S)bN*O!P3mF}BE>dCs&u%E!s*c8-1vF8$@9E&LVb)i*3Zn3&w- z{A?g!MBAnw2oMNqNGdKPVHK$UtZf+Bp7XJ6dsz7g4n3Nbm}vs^C+9sIOLdF{VDSFT z2eK@~>YrqfJAZh%FyNRh8LLhR5HIo@JvXijLo&1vzS~9|+?5ltKe?lXLHGSl84zZ{ z?&zExNDEW%`PVdV*zDcBTYcqj`6%KHUxyB}rJbzw1K#*b;bwc@bMBvA1} zp6f}XOV5VqQ;L^;;lzET%p9OMXJ=qWh*R zY86Gz|0znazF{f+Hc1krBkwyP?!oS6+d7`K&_ZcAAE^5fPcuPYda%KRl}7Hyv$~oi ze?RCs+%3&vt4h7tb7p}ah&j`36)C5v+(q5l+%9{WfXEVGr~bKUlh{nVFl?iNp$Xjb zPOM$!0w^H$^~6m(0=&)W;772X@?z!}?WgEZL5wYpa{EW>LVgSMybS%`n!?+iX+{gd zgesf0W&>*GKt2K$K*SyPu!Xssc5i&kQMMNN9|m0gUi%35!15P16H(E#rUt#+dE4WY zrV64j>P>v=*R`{lL*CL}B|M~p3>*HP;{d;t`qoMmhcq`WZE<_~=H^meD9AT+wbM?- ze+TpQ0;%$F#_$N}XYW-cC34r*IH6_&Pd#m~nag3k9=l_onJTLxmI zewz@bk5R=eW`UGtjqdou1{h!5_tbH^RqNUraaUFuqnYO0- zINg6|Kt&$s@Sd`8P;^+_=UCdnf7a~nyn|_JY09{A2*TC06utBbZX@ifkzHULD0THM zPscP^mQkD7top(0G83(!W6s-MnBuj;7$q=tzNp!GeQ2CUZ;_|f&qG&fi!q=-WZTE=filV*W z#P+QYT4269ftsSKp<3%<8*Y++&-no`6giG7^gPUkw~@_h8}MmeI`1*Ki9B=~afgIQAt@*TZdeL5lDu{<#!<-64hbQ(Bn4cFCPa?p)nFO-O? zXle`kI0A?neV$24t&d)U2btG^v+T@bc>`)3cN5i>rDh<67PI z0Sw}KQ51S~k^9_kaS+$<_LHKg@1(T|C|D+q35E_?TJ%5mlKiL%K=2zMjhtne_ud*&+*p z_7P`7SVwTXQBI@OKQ3!p8lvN?8fK%IH%_frk~t{7XMaP|j3v|hfPU+T@gKdO)e*kZ zvh;uw4Ocs}NrM+pciALPkUXtJVk3yPb}6L$yJO~JJbu>U4i)gmMb^^CTeqe4a&?mH zBw%K#qZ*6H)4`$V1E_9TTCAa))nq{EYN~|zX*h$vUnR@CscxK;Ey`qQ_+ac#Z0OkW ziIe1Zez{jYf}q6W@o^oW?A1ehSx;C_VA@+(Ic!*ExDe=5mp3|lBAi$8emGPA$-C)@ zUljuE-;-v2t0(5wEJP(7X4cgR>otBaaDYFqR8YTJR#pxIfG2?hZHM(z-fALx#hZd2 zYb4wUVsvSV;iQWtT=5bm5kRRN_+x8iq;&m_81U}G(KUG8M~i0eEn9Vl&u-md>!y!y zDSo3wbW)NTz%8#}WR+zdv~aR*D1}KBf#e;3jNrQNm^@)SzFQ}wA3-+WrlUDEa3TX< zmPt-cx3{A%51>*1aWipU+!@Zr&J%g>p?9nXf9x&Q1BiX<@Mzw8YVeA*m=&Ffzn#~A zzUSS_o6NVq|HT)ZkvuWL4 zn(@oIPHWN+bDnwx4Jd^XLuxB7p~c#L3GpZpbA9f}2W42b#@ z--vv*o@36bU{mYI>K?gc45D;yo=ePhs0bT6hBV*3bxLqKPNEh`c;m5SNlq=fBOTTb zL=}mf`*jP-Qtz1@s_*VhmpSZUnOo{hr+3~a^vsOJ$@b1|4z4T=4$tQ&mDCmG5{o&= zs<>=rU#XwJmcPEWHm;vM9g}m=b)d>zjF`En_|RFg(~~WMl+7nxQd(70krv-xI*Nl7 zDs9n!s3X;scw-6g`o7k}Wj-C)JZoIS&U~0;=m=a2!orwNz?eP zPw!6Q{Y&C~IrhfsmFJ$5qrGwMr_{PKdwu~{Q$yvSO_XvA@eGnu&EXfc74khEu2stFIFu0Ufe$R1u8qsFQK~lV`;r)Y`xPX$fNN| zEyKg_6iM^>Pha%9z7CY8FsP~SH<*W?R&8TxnM#c{mau5b-W$p)MQz|_rV8Y{E=pU`&AhaxR+Mgtj4~W(SQmx& zB$9DX)jx<#ASJ#}nh^83>lK1y)XjH}J{RK9eC)L`II%N> zG!1M2Y;Qa=+@p68a{u0c#8=(!QRB`JEPSPXz@5qyusi1&(UnKyuGyHlce7kQ7LcM% zcR2^q6N$R>5;+tJ6%8dgX0j#G*0Za2vA_?>S=JB8GG9(C?kIFJf1N2x+$0$z`I5!K z*H3xIGW0lJbZq0Svbt-exWHvC#0lcd@g%-j_lBSMIn_xt<^;X`UEmcNNAgdEy8 z@@Q0E!hrr{ImgrsV&0q0mm#)uFZTAZmkV2a-t<2I{-*MJ0`wj=8!lR59JlmN`{m4u z1Aeh}V*IqN@XjDbYRy;Dm}$3NhRY}RR=rT0eUgMO-+C}{i6yHBk>;Z?`v++A*vQvk z-&FYOAP`+bzf`~@s#*X4LGd|xge@#8M zLZQO+hkiCK?T5BcV8*w0Sdk*6>TRA@R9H{#V+!`B(j3fN@%@@2=|dm(R>ef^R~@n? z9Im|SH;_wWXbm;_u2PF8`H`5>Q}Em;W|UsgTey_shtx{nV10@f$8l!4nRKSTOY*1i zTD~Qk{q<{BzO`23-}YOA&Opk!yrwrOpF-Ew22+$T{@XHQDhiGapi*{e@@paI4Q)a^ zl^^>kI|n@Fk)GxD6Kysu-3!O=p;wJm(ntIIJlkImN!HYs*ZMf+*j(60wDBP`Y^&+X z`{J)3>0Wu_Xfj9QXA&zYiZnlj5SYq~rqSg?N6qk?ELR@l`ss;)Go`~6qgeYi)SlIM zX_>r{iLohmUwiw<`~DzXg3EITs2L^|fjpV?pqz?4EvH(!lClqZz8tKwR;Ef`l_V;v zG)vmYl93a)d1`eK-fS~)AEY8|{~_H-<(fw5j3SE3n}$(4-*cTCLCatmr(J|-&I^ge z-qwmO=cz}DCGF2pc1(7BY@OgAX9?K zl3LQ0>TPM_>kp5fSlZJ7ayd>OW~I2Uc^FY_s~9o%bMX6^x-zVi@37ESjGj2sO3m7H zlk<>q(74&g$^5mB?lzKd2nFh|Wahd;wobk?hUf#%q&b2@$1zLnZ7y z1Vj6aPBpiDMAZr$1R(pZS!*?F;R_ZuHP}=WB1xeNdEzVBruUCz^>+T@Jf8&XspjxdvCrjM*nV<%xbjh2=dV zZb7jp>JviVUt3?_52us`Q*_o$pHwux`lA{3a{gg&`?n-#&2Q_z6EnxG@bzwfA^#xv zSctlWPb^PBL1eHOIGQF-uc2^a@AU)v=pWy%LZmH;?o&o&?#+>cCP*J8KC)du4G^h| zKs+EU=I-lP6<+@$8Owi`lb+^cy=Y^BjAtA>`hA_t@iwj)rb6vupiy{CbqonaO~MU zvgb-Bg_q z(5iNE45ioy@yXS6RndH#VNN^+wQ8}i-{=%F+2FByZ_|G8(-!gbM5HF?NgW@J{+?!i zE$TIEDLqjPOatO-b!;C-lI}g#KcHJ+uzkt)-9&p#&takVGsqRio(Pg>W@C#{BI^Gb zUuzSdn$h(FhufQMiP>W@(&b83I_c6QIS`bvc}cFqn(y%8BfU*d>sI8>h4E}WOqdLMpjjT78Pr+ssT)p=c!?~09GJN0jlXIHB zdV9(%R%%c+a_UQ3Qj3LX?M@GR@#Y(VJZ7e4c3)zpM#JhOd904+q$Yi7q9=h%qtHL_!+)`t--!tCVC-HLjo6#}=e z;rAQ(<7xa&_0(G_YVwOQJYO<7S3*`!nprbqj2&|@<0f27C^Q^h-%s3L zoD@BYmWpx~-uFjp-gq5-lXM|;1OgY5Vz-7{fR>Emjz_7=DL|v78A$DIQ$476UJXbVXtrC@nrAOwJQ{RO2?Q}N+m=n4s>isvi{ZoG5@aHaW-xs z;PMb|s3et6B7|rcrkLiYR`_Uf1PPCe4ZZ+jUkFFjkq8=P7AqSXnV1_V&t=n3HnK(r z*xg(L0BM75Ye)y?8UcX2-6mYpYkJ5n+>BoXllk+@`C)F6x( z;n=h|@JTqvs(75K**WwZdNAIKjif5ekd}$iG6H1)MXrqS8EG4TQv+*Fi)29&9;&V-#W?cFVH|wOmE`4({#q=duIPSrN{CRY8H!c7V_8l1wRZ81Id;I++HKXSeMK zOG1m!W_6C>{i8ehCmcWS#@>`JDj>V+%i}mcX)?fXBcn++w%BY*`w+gZPq6Jc)TiOK z+Ij{A{7fkwM6Nf0S+NBt)`Hd{VWO8?!>@sna!58evf8o8KRAbzPc#@e;RuEdy_Qh0 z6lEX2+|)>!6m; zkPdzHGDf)OkaBF+l$Y@5J6*i>?tAp(~+*}{`guSbYXGRYw$^Eu=6 z0RXIA)xv+D-OzsDMvTbiv{N$sR;>>6%<~&ES&jk@8lJ-`C-nzl^_nm*ytJv^VjVs- zn-fpy2f&)O5&rke=GHz1Lx*N_{I~i5ux@>XS6(e^PqZ97B#Unzo7uO1eS|mPs>r-g zz~I3lzA>v5fQ=g?y!m!z`&b4I3^H?OZvZxJitz57>UOv7z=1(#OfLao^X4e;e^Arj zXaD{|jyS9r0A*!SKKQT}Au@gT?Hl0GX+@cRtXp1PlVH)}hW4=(7yB7IIGqpDyrg{dkutwE_xMrUFN6~C&d@y0E_>5 zA4~4Lhb&UyIHcNzDMPJeu*ud4Yc4kBNnwG}zAq~Ty$W1Nnc0_?UmjAe%kl>d=4(%ZtWA?hSk2$?Xt10 zG-g{V4{5gVGYp%SNNYS#LOkJa-v<`;^^r^!cnO7tdiy@dvDi}H+zg>PfEm*>pR4Ak1h2ojseLRHClqq{VHt61Y>4sv3+vn8H)T>GhfZx1rzlT6y(V)$ z2~#H*GG%f`oT{rMJn+a$+6FUiauE|Ji~^vlD#E?@FKhq(9X_=eB(`W71chnIj+WlaTyL85+5cd?l`@SBom7)hGr+|<M z0aB}$p{FD5^SlJmY)Y2L1sfgc^Q{xO;m$i5I(RVJZrv}1;_HF3oD@4mIu08?n9IE1 z-h#ir2Df99NVnj$y75wuph6m~O+wPJ*^u8GeYb@PKv3_^V|_~tQv#B~GRCTO8g!lA zOWl*ZomwEtBvTAF9K4Y*$#9ZQ5sgG~K0zZ*&<#G};Br#*@uleJO){-G#voq`j{rkT zgaBO=7`g@?2xYA&V)dnr(U==oqetp9lXig|mtd4%VkBYdD=+at@7`Q>(@hi=6}7wG z6beNKB~FSRQom^{Q+9Cd$Xl(3niLE zC?2Ib$HEZ?+AdfWZOa9=oz-^0L_=C_ApnU-H_0|^`uZg~o-{s@CR;c0ONU6ApF}uL zT|{usG?VFrnsMo1U;zG1*$CZ0Xdy6s;0l7vmszdN>jsYwE&;lvc5@AS+QFMzPI+o1 zM(JQY=@dq+DYM+XfGk5W)GbM-ES~w(-He(vnQt6(47#qWLnst&C~;D3$D$|iXWe6u zkmG`MI*#c$8Ml)ITP(r4q@>R9&|H+ur;SZyyAq7emz)qR$J>%(p;>~@5hLi|zrS+tDHN?JaZx1}WVN5fLna;SigaeJXU_(mMr0F#E zD#6IgrYsfW^@@$ysWhYVV4A0nm?>Cg7USN5Ksk;>EEXda3em4$KSH4pxp{f`OG|04 zsibb@XAE&WY>G7D`G`iNxLhtIU^)V6gM)*YG^L&txq6J2*11f!1D6e|rYO&% zBmh3$XOlW zkEFqdwTlmj@O76gcTzc7M?REGRimx+DoW90b zR+8$fd3?6$uPj}%n8<526s?Qn^8_)?j4)|{iC|3%swLF*?uU_`-R^1=LZG|-R22;7 z-J12JlW7L~Z6>B_i5f0y$-`x2j!mUfM59p#4H|^5>jVM;3JMDFy4{4MQ4(%9>E8W_ z*Q}?Xa43&7qZjnT(Na;GZ5Wd6qG?MdE$dSxqDf3MiFDHFx{b>K%_VWW!Q~Pd zMrIoqR7D-O)I&T4b8BF60;cwX3l4@z3hENgEMD{>$A0@F%tVaMk3N8CszlQ>Y3czT z4)MU8rh5MK*S}KQuOBBIe>}!+q|z%CJrVT;d<`+vF`Al|FmK*JS+{mB3zzCNElqPs zS(3s)5Gx&SpUdO`n@xv`G}Px6Awr?;(#3@k7;Y~$MT3}Iy8)3()5mW!Ay&ZxTT<`K z$1pT(+s3jig25oVuH$mKFin%{ni`s#nux_>#PSPitlCH)!y&u5fu{T-TrL+bVdIG> zh(;v2-hBMoMc@i%YJvMBNVy5EHCxZri?Ey=?3jg>PLoWgNu@HPlS-sWCX-Y|9aMTGn0)bfY1z1u##f)i@5GUTK8d=TqWiPOB;S;P}WwK&*7SS+_tF2*>*NbMwG27N-lF)2P zHYTCbAH*!khn@XJF9e3$M@{}9-mPBGgp^5tmyK+xH*wv-qw7d1F-;TGGzo{pq|<2}#{nU5XJykA$flt&O0jO^s;@^B z6kr0i2^Xn|pv8e$xExKZA)ZQ8Ri7DiTjIm|HJj1Q%-Tto5YSrUrL6$0X&6X%fMnAa zVh;522!dMcF@Ws|1QJb(Oj3lN5h*D^x*!sV_0>?-Bw3Px#a2d`t{kV~)ib^50{l7z zJ%S>i!;04)X8P>Y=|6rPhn#mIi`K8kX{2B_*0 zimxsDO*h?iQ&$f{vG1sbNFPwVEu+H5^)y@4R0#R;D#y=GQcBE zZ0Vo@wv=p4!RjQ`6&7RX=I&DU2O$Ky%S|#EqAHSvWSTz0B9M%ev>cjUK0=`&Znqo9 zagb7C+jjf=HMg5IHlnefd}(6FqErNPiC7i|kuYODJ{(y{IGm(xa|Ly^jg&_nR+@q! zHY0T##}?Q^Y_0z%M0?>-0o1xeIA)q~JV{Ez5U8|uelIf5ss-7 z4_hST7RjW*X|;^n1Vtcs{K1xxmJ-`=k&1;$N(gG;)!1%XB}caYI1Y}TS(*L^_$}GBiLJ0C(q`%62YB<1i+J{#fAHAj70h3dOI1w} z$I;qDMiTIwDTajW$u$HzlGacYX-BdlEveOXnnT%Wdl>sLJbqHeC9L*%*qDMsY(_NK zkylklB9Ux&=UJAOsjl2AQo`jXm0v`I5cq_ops4}3Wzjznz%UGwX_HtyjsplywqEy_ z*VYqA8g(y1Y9uX(lr8A%ZM_-W-bVz|YOQo+Lwyn|8=yK03zJZ#3(gt@vr526rbeG* zYe*ZcR`;PH1U^IHbxR6;5_8K!KASrS+p-vbi-u$D{vV&)g3^R95*(r;Y6^4@ikG9;Mhc3AqK1 z)9PAtB#n+>OBxdX5PDwzZWdr|vpOXG^m=X`QN|yRNfXP0jVZ{L7X7O><}!m)p`oKpsxo?gyf;W|DF1V2Gqgm8lVWdRZi7ql*nwk?KbF(leVwo9N(tvz)-4XjH@qK1oDej(a! z#5^nsNQYb_$?@6c+&*|ShlS!y=%sV@33){FT&zz)E*3*8H=tKl5RXJirc&)0e43`= zcDc}lK_a05Q7I_EVX&1XOA|zzYe^Co=l(}lng;MN>MZYXp^%j%C9kpZf?;mR7Z9tc;F~Xcp^bM zohF@5lSm|pgu^uYg4D}Q)?0o$&YId9VzCx%NAB>N+G^|V2uDK7okc1cCv7`e8uT{U zxw$1F=13ZolDO@#BnfM!;NV=z1*PBtY%Ajmm3F3oX@O;fbOe?ou z<672z@;Z)fp=laJk3I&c&j74Wu_x%GLkxN1tKY&T4qO4o@AKrZ9 z9zOf5fi-LMu&gh4!3ilD5R2dw;6iSla4RGmO-KMGPMVyGO;iPfv}ENG_xiAWK3tlX zF%!tdW6~xR`w|Qbv~WT}HDkRHk3!tZqF28`1WHN~K|exTjLQ%5y(5bG=ZB4i!wGr` z6ROKtqZy&uq(9p)(zjrog}0k?^LKf%JtL8zAl5>GS0L?lX6bP$sYpS! z36{&vkb)52NHcv>VWPERDhxM`t{`>6EYhJYG?$A)Bf)V6^-K&lGeB5aF*o(m9DKoI zvIq($L)d{VefukwU$EGq<3Q3Ez)r<66ESKnnC@q%R#U{0giS$8W?Xrz zBuw#3zL^ak3DT4}?o5$c+mQ$jl4(KO$Yco$VSvkrBL!*4Ln01w)21aECxS_C*igO1$g5vnYw#IQ0$frNJ4!w&chEs#L1_eqQpt@wL*#00m|pgALhL| zFEf9EpuXPEu4Z~mO`}hw5x?f(({vmsxu35RQh(D-?XAZ;I(z37n{#uvvoU>Vq$@2#?=GGHFu3rjkfY1&L@ArWwh^m2~jv zZl)GkydHsc3I>zL*t{NLrYQ~h$V;1KONS63{Q>4T)N`;q;~tCH8acXyvxAt^4z)%~ zQksUP`;hT4O_n6&7KF4N94|A%6tM*{NAh_JmOC&p05kpI&CKV;l9@@$S~_E8BEcmz zgb@J4z;X=Y@k}2v+ajKZMARbRvM91`di!H|>Q}SUk(4zAZ*Ifm9+ ziwBSpx1rvG7Q=-xiw?R-lMjp!Xv}SK&{HswIFXI3iMkAOU6QEo zA|^C6X=6!fb|709wAcas%*odnYpAZ=OLi3H6yjPVGJZ8IS{3X?5al7y8G zO!B}?cP3rjL10=E46sv>@CXdui*9&Gx;&URBvTr3X%n^6L@W)bIZ0kRK`+~(S0Gb| zc4G@{N=hO+^gi??jyU=hT#*zX{py$atpt#XJ@s)zP7*d(RPxL-|7FsoiFnkK8j7z8 zN}LWbnwra4vEl*dzF$UlO^`27px%l^BP$lel}Z!v3hY)_m=KwDg`y55va<-d-Nc0; zWzjCGi>CU-xz~ zCkZJ{qeXLrWfHL*f`%a5a1b5un}nz(iAX`*mMl)dDk&J~lFamjUlW*QDw+#7q#PZW z$B*uJlhWO!Y)LEvNyjAaKs;^X2`9*jS$G_W91o-n_^biyYzF*A6<-UKI2}Mp>9A(yzj*V_ zd8}O@z%+O7*JlHz$rw4p!Mn}AS{qs1}6{bar zO!C7Jm*jXKcm$-R#I`kbj|ao$MfZA$8ycyEB<`5RYzG@j+P3hv#3)KRnXx(0=)m$g zBz!@#2lQg<>ECAZNoP|~+6Rx%kEUfR0)`3;C_U^5HY}Nk%W*&x=$c@#M>5ZXn)U1X z_~VZ$>D?QbT24dpH9&pJ4VNf8Cs#uKbP>}NGW|H9svQtU2 z;t?XIL)?O;mcZ+RelEz;CH~g2Zkx8Ckr5~v)c82+kWRr6w@tGIk0U5_flmnXbxEBm z8R~|WX~-86cL(c9BDcFArqBor4=&51F>Pb$kf&$jMQwTK$yS$UtfR$bNRAJHAvBVj z4!R56e$s9)QC*TqnZjHgyHgRXZ`qtK?WQ)os~~MMzE=@{oWKAP$FP$Z7mBIF688sPu_WDc!i<|p+4mY z2>+CPL3j5im{o-C^N~s<@kd+8sjVOqjS|YrAr{Og<@SNe4$lT*BG}G)N84kdcg^GKsM#p31}-({cNJ=!UU#mz)sf z4H``L&|z4gR%9ml0xki4by#6KtX#Q*R4S$7PKvJuN}LV=q?FXxFXE$*UtrT_H>SA< zR+FZYlTH)B#E_k>jIa$25@z>Ka&E~Mym7D_Tr^o(2)!@9ygnE{A9mWJv8s~Bx@wvm zs)@(L_>ss&7@-NW^NNYNf;1ZjKF7gtStKLP#2qM4XT&gHmjpCHo?GHZV0UoM4Rr~8 zL0FiO7y@#UR7s5lf;{06)FllTl)7O}T9R)F+&icw?vN>;)U5k(*(UK=3>zpi1a2Xb zQV>HDwgi?VS(bt=5=MA6rU$*a+-{`HOA9)2Gfi41FkOIdgzoi{P9|xns-mH;f^btE>0}hkc3LOHX38T4q(s(Np#eUx zkCg7E+3h21a4&+ENvx(0CmA7OLyIk`wIQTQ3SAlj&B2iwORAqs;uEkS2AT^BbccFN zkkB=}ID`@8i%b>rMjP^wI9kRs)XA))WTIiaTr?S8vXU`coJ`e)EF&Y3VMowtN^~4P zPeG;7I5ai!@mosSKz^?vCDir%1 zB~Awbi9`ikHhsvZEnYNDuqUf1=$45tO#;H9eGaRHqy_aRlw`Z{X#H_z4a5`31)*Uj zQ?%69(oj=LQ&S!3L=&c!5vA>_G_)q=xh1$$aa=NiR}&Zo2|q(qLy6R7zS7QAbd!XBI+q{2rQdVOt3z z5^4>B2!@F1ZhUCO<1tdUAmow=2byd_v+ZC2E0VC%795hFLm#h;dK(gs30{ewWs_r@ zgd%A?mW^LZ!YRqBbSCA#NkZP3@f>%~c}$!>oqh)$M8N0UWh-fi2tjuLff$9QIJFxx ze?$oI3PBN)r7bPg*Vn2-u!^q*N}RemQaWteJfHHa7}1D#kAz7AhU1{6E!;Y2ZSf`{ zNk~DHBbiv(k3M}5#xPt&np&{UBu$MCG&j|e2-jmdR%X#Y?Muuyk@?3Zz$GO{ya}&p zA(d#R!SE95T?#@cR##0R(u6J@Vm53_g04ZKA?alZdb?nZ(3l@_Fx^n3JA|bmAvGEt zhk9G^PEry~rYY9bSeB&Lf`t~yRFn!J&uh>-Er=uW>w-o{U^$ui(9#qvvmra+p}?il zjD>JJ`1}yEEdot3Jf=k0P;W_=rJ>md$L*%?%o&_;_IXS=>~IQtm*Dk!_nMn6t5+}F z#f4b48);jp#D}Dp2Bsxhvt}*nbQ(h?MJReW>J#40v7I!_KYx);8wFDCzCX{Apy`lJ zk{}kE%uI-tWGY}Mq@+bcaWH@rjZ@iBLsMf7X)8@S7RG8_fN+m|i2__g;F1zgI!4Hf zld5c{$rU8Kpct!HZ(>dLcq2^&%``F7p*jt9mZU(3UWQ~`P~+36LrNF)_ei!#$ulv5 zDJ4f3FwzG>L+9UNhaoP(D7V0t4udU+CevoQm8peSXh{lmNlLJ z&7@F_L7qA`RoiH?L+x$>Q5zghkZh=9b5kYpNE4=G@A7$e36WV97Qn#4pAHkRh!8eB z1oKOf*?A=5Q38=>g2^~>)1=1CxEnpX!w3&7Pf5y?8Ea>lkjxA~k>5?OFGwJ}kl>~Y z`h>h3u+2GEGLsf zu4$5ZV>K;?+}#ZIJwgZwAPLw>oZ5}lI1no6h25(Ui9`Zlv>9JIK`N0T*|zM2OXtm` z%`6>e1O>&RAbGik*vS<26>Esc1W_kWb21~mZUOy#2Ch`bHM%;IwGe24V`ZIFEL?)he8Z(Zcjoa(R*aue9&TfvUuo#4nY%P4K3Gi!~ zWjZRWtBA#7`+gh>MHi#QshhKQ?VFTW*jV-#FO@EVv?B-_HY2@BCU_e;+*3~=?ImpY zrp46Jv~oIz>uo1vIT;Rk#eNR5cbMZ;qJiB|O|-rSZ(c7F`9;Jn3r`}3FBzvL+Co{< zW~c^J{DR`FY;r>(!VT4=(+M)(PKj^?LT0LjI|z^##U9Xvpv9KdSqi9v`!Up(F{WOx$> zWoXFuYC@KjM4Ze-PmkDXUGIHQtJ^PIg56Y&)>KQt8=}SICK<|Qoiu2QHE@O(`sZZh z)1a}wqP5C}zy?gsAmw%w^BL%760eoYRQHxpY(UJNNobHssBcNJ)`A8LoIr>P=by>M znKKzUZXCV)_s8Y&pnsLDyRS%Yac?wV0HxN&n+T+J9N7=Y-J4)u5iXC9hH#jscpQTcc?~jC zYcC-)1x9^hYvqKlL>Gb(4y0O;acI`VEQ@Q55K!!Pp##Ze3$~qEbTFzJ#68*Au_!{< zFwuxRg1GRJ<)lE9Xaf3qpfW93W=TGaL$4u2IO2@6m~!M%3>-U_?5r#_UGEl?TeEr< zA%a01GM1&30vEKGfNE}T&ba)#P(T%meT@>QZjO{X>|pI82^6`K%+70Jyk1XsN@Cl^ zNOxa+pUgP``d1we}w5=={sB<7+2zMM@IV6S!;#O=@_h#M8QpypAMpTf}t;lF3}`<&xxUuzC4%p8w5l7(#r( z`nh`#O#_!M&qg+SLN#jZ5dL-kA#G?Se$Nw?%+X?^fym*5NKkWQP_RaX#hsHdr+ zl4Lxy(4T38BlYdRhHWHfYfNTFAd?l=ox}-XMGDd>iK7Wz(#95T$t=qO zXqtwuIq1lY)nP--2EWKyT80L_4A`7bGcH`wsV?8XLJ08a;Pz$~Ec6)Qb%DnV9yj>> znSFjYc>Ee(w?ji(f;iBR--lsI*B95niSlMK`An4MimcG4gb z4w4kTaRrMBWaj~briMluYAR@HtRWt2!jcYJhb8qKk=gE;_t3iJ5NX3jtuKJSLvh;s zUxJ;YI&D*~I>f?c=E8A+h?T(5O&p=&5?fd9jam}F$hgN0GAqZnYmFw*9EnE<+lI6a zo>tdcmX=wzqb`tx7Y8Sm#0A>E-(aA zxG{yqaU?c2w#c}WL9rKiuTg|-hwy^+tTNmPmlxBH;Ui-r zg$yv;u0_i%gCVrm(K`~iFNn|MBOR_sCo=}Ub?vHb0j`QoIGQ9J@=`SNM8=(T3fZ}N zUH$6^ps}`rMK3HQy?LuRNodd53UOHVw7aG}TqpR9{0f+1zR=u~Sik)+{hh(6Mq6&}v;tEG^?c)b`>;T*t}8 zv}^|)O+&gp;P&GfUSde1wnM@+aSRhr0D5KF zVp<72Rva`zNP{|4;AoPzXqqm;&~!Y$0NL632*X7r-h^dkG87#N%@&mA=i<)FrmzS$v9p$G@j4WsGSWFIpBeBWMzjA~Iz5vqjkv7~Uq(j6^lXfJ+XXEinit{A}z48ej zbU1odgvh5$h}Jex-_pXSG*p`q5jKV|K#DZ}=0qlQO&4S#Njrj&H;85=$Gv7dMIx0>CBW<)2Xn}z{sXvOyT4YHLUEv>#HkCBR+`kt`NUuOGcEJy5Uq}oHcN14 z4In2sA59pBv!a^4XxgN}{QMemYG(Y+>Rx(a? zEXkU%8V(~Xj!_1U?>kO+JxImf+h&)f|MgO7ML!`FX%<-fc{8F($Y{% zI9!V@Gvee3Sf7?0diJ?o{L5dV0SiC)fQA2knw;z$%y^V|btQULCFM{5n+-3$K+cGR znSSNZ7Ig0nZKsn+5ImjO@gUunKDSN7Lx%4 zb!Y+N5`4Sat?gveCv3n*1H*-M1(9waF$57iNy4$fW#JDBin1mD-`;tLNmZWx|8vfn zGktqymxZNCl_nq{)!w2a#sZcQTP#U@qcKHey3rWZO-y3)^L^7zVpmiY5EKj2dtbJ< znc3-c+VlMWm<2%v7hH|Xd@inwYj<|)o;~05Jooqh-o*tv0lyo|JrKJ&MQZ6{!lpr| z1=}(Zm*6Wcr)Jy;iR8VzCEY4)v}8ZBtIVcN*!3FPJB_xm|@?p#iuGlw^pE#u9{{zGt29jQns@rF9Q z@d&Baukq--3-I(G$k=&Tal)L7DDGQ>FE8&yJwB455E%Qm^|HSdOJsjX0ENZHIdRJI zsV65+A0g7QaN4$$TKNRACm$fbx0Q@lff^W2Ag>a)&r2qgAymJcaHySFG(;vH|G>|y z171yHs5*|w;ORc$xip0!5>tSA1hb+Xk#*hKfY5ZL%ZqgRu-$HwQj)L?Od)aQSyUD% zXg(Kt`JfAvSSE?y?8DgmDxHp`*@V3o#03=9)-qz+3_g4Fml!vG+(BDcC!cc`e_gtW z!kR%SE)A!7&whatQX(8k#3Qt2!T|e+BC0@_E!mldN*!Lm=PvT{f}DEARrq{fG*xBl ztXWK*HH+PKb-eNDqlDM3Cc0-giTd66%`{q5J+I&WZC<$x5V$Kpr9bPt(W6dPfnaZ0+7g)7bnlZ65jz4Km%)Q8Tgg2 z(A3<_>rXsRY~y+wHg6?Ww;R8eM6=Rt{_`JL`@esnX8J6~TzVw~PMl2Np+kFBERszP zSX~jkygd{$BP0o+yu1Rx-`|^uXO0{nOLF4$AtOS&i7);m@t6Nae9JDEFQv0$M%a-Z=LTHz^w=z)s0X!WOp}@rb-H3*+`_6l<%5$E4z&n z4ieh|;ZngHKx!ULmzRViiQ6WYlEUS&Da?n8Qk9hAr6nv0HX9`3Y5Zvu)l%5vNZM@J zWka9g!#U%ea~V5hCWB9yKvhNgdu~(qJ?Gr^ zHSz-opX{rvs+fDjjU+M|cCA@M>*md@S^N@vSH3}2zD+TLjup#zdihdHYx~o8$_$P> z^D~S%^DF|v;0HTigdo%0h?Gt?Rh{g`KwLmZ!r+0m=3(@v#*av?oW!Eztx7yG;BI7LBv)_NJ*Uol_#Fa=PsDT>&@3$de>KJ+_;vFDcB$t zuKUI}89sP0JAVHFPHQv4;!=Xc#-Mh*LjHh3xO9bNI*HR2p&gY|FT0!}&y{{lHJ~x!s&|>sQg;@1BFRzalACAmTv6k?3l7qpYfd9zeRi7*Zu6 z9Fn%caTzFHNlBhUO_@%MrLv-3qr$Z48;_%A93qCK)fRL)Dk%vanwyblp21yLT*0vM z6R7Fi7x()(-&%KWXZd$-XYZPotWUuvsdCza&vE08H}c$l_t3dz1CdZC>ES-kJmYNQ zFD@b6*+c-SPo=1EGWg9jo0l(R=;+a?ZZ}hA%z)oM$UWCxjZ(LR0i;;@o1Zao>Pd{6 zHXZd4BPhI@#^51C7(8SM11Fq7%jZAOn=dY6-M{`xRaJldZjDHL8y$-m(fQ`9Z2iB7 zs5xg2CtP*~RYQgxQC%wD-ih56$~r2$v4E(Pt?|_k7)V}TUQV2HeCo-GQ*R&zMz{%U z$?u3S|0nUfR#I^dsnrswsKxE~V;BaVZ7p6r4@r~`L`9%UiRtnn z?F>@MZsU11g<@UO>_Av?Q|iw{y1hsgl1S3FO-dRF-NBa!W%*E4;HAlg=bGJAcg2~U z450{UP79iCNJy|XjRudK>7TunyS{J(wL^wbSWwWTlWMH9ou%LZ7Q0rw%9cey$9 zs?YI*yYHrP?HZa_yn={@F*P?c7F>g-D%g(1OeXNV1+uV`XsC%|BgKxV{zLL}3n=Vf z8BLi!jj#Xy58QLjwNSr<{?QQ6+;J<~JwIpolqsm%;r}H1R8-KXqJm+gMiH4epXZ-^ zf)#)LGkr=cD5&a7Jlsii*EZt6{xKW=^dNnv&SdPR^B8s7ndm&`;mWJI#Ld{rK2H>N+`jtv{NT2S(Ry_o*6t3 z{z&8E=P?oq64f$*;UVIiX@PUPf^ zFQvVpkac_a;#OS*%leQq1*Bq_yEn7<*?+U)>HlCm7Qu=tTs|+_dx)TvlExKFsekkz zXo>^fX7Uyj)>)wD=QHoxYZ*{mn{(^s_|%iT1UpKcWCUyLOT_;3BVy~;kVtuu>LB#I z8uAK@P!x@>a3`U*X1YSHq>>RF@y>YCj|e6Ei`Ioj(w&E`y6|9SZ<0`=DS)8K%pg1# zo`6JmL0N%<;&q|vIxSI~)teLsrc#__MoBvg+qxZhF$wu)emq0RaNg&xVb1w;C@d;E z;&C2=ycA$ql*#69uOgs0ygPeJz$BpBz|N9F+yZI*4i=QD_U&o_gyqb6- z$|>`&!sqq$=rwDqN`65BS1nk;l?xWI{FPUD?vY3E?brtKD5*#Xd6@`m+b-7L{S|in z^?rt5bTzf7T|lsU5dQqU{l`Qy7Nuj|8dSvoRg>*x$H^yw%6|RmS5uSQ(#vs7kQ1jP z#L2{P>emrp@_)o%`Wvwh2S*)*D^P1ZR=-b^gqg6Y_(io$DYDq+LFIrj4db1PTS3q)35Ik>P}@03MG`O(~6uyZp-=_D`S@ijD;i`kc5f!BRxlV%lQ#*NT#EKQZ7D{jy9p?viLGl4m=~nu7_#D zoK>otoL-!Dn@JewsNt^XtF^>8}aJULeI$jE> zW*f9x$bS=VCe8XUh`W!=B~L5zEY{$k(Rj{v9FpNb6OU;$c}GfizA?_-yI;raFtnZ! z6t||QW_Q3|C`JLYxt$rX2BSky2M>Ti>um|3a*^nStyAN#>P_z?5hdoDx%^U+v18NYYDGWV%<0xjiHf*2$p za=MYdvFZrZ@6o7$ zZEd!tW=&Ds6oq2lL3DwD9{B$6->sf4hs6nTbuW{G5p$j+Vk>+_GIo*97iIk*w%wDw#bY`EJy0uRgm~kNT^Y;pkV)sufWx z<%ruTN_F*s^9O@`*;dW8!&7sz>3=~6g&QuFN;c$B;_U+VIy7{y{F_MnE$#UQqTYyhp1l zJMb&)aKXdeRUg}f*mqTL!t}*nkB+$H(Lz~d%eB& zX~3YGuhmN6@-kvm1LnD9AIpsKj}{?K#ke(kN|YG$%~4TzPdpk5qTqZBeG6z+m!Z*k zmBeE_VaZ_G0c67>^0hK*@b~0=v49F^P8yCJ-*P9`Yq3mL>L`Qdi1csluAHBIc$PNv z&;};BbPo#ntn<9~ep||+aMJP43i-fod`x#><9sE)t=pRa%Gq(rU#SuigKDPZ{;i*~ z0HMNnee{>oe3#H*rZ;}%hii&&n!(u($~T@DW2w=R^;s1bw*i^mi;tnn%6EPfMh_b4 z0k~ z4?PK!JrN>%731Tpe=u}q0l^(!Vnb92jn4A=-knGwd`Ljb4n!-8tT?*YztDtaA%Orf z(TYIcW#E7)LIlT$x!ACXKdcrVcQH^^v=s(X-j@Qxg)kEqFh{Y@56areDIb#6u!wQD z;gRc7N*E_vTro^xMNK9bhLiRC#AIGgnez$kd6mB4FjN17rUDPpt>$0o=5Z#_Xt^>H zXR`|EE#*>A541{~7*^Bimp}iYb-Mt*Hd>`OWqC#IW)X1*iEP9dvYL-IKM?BWv>VZ> zk_u%b9q0@t`MibWV_JG{vubwPVo?@A=tbLQvv}gQh@Z(HbUh%^)Y@DSpT=zi^A)s6H{S+al&8{qp0%neRSwXQ$cdDj#zVQl`hitCfC)?1ULvA1@`2p360GbN4yeGNlBD zJ1k6rJ%2EK%1ga8yxA7GLe)cgxi6BsF|b!V1ub~mgWnf3jGgUCdmxt`wQ^=Uy4=9f zzC&Pa$l5+VZ`oUvt8fx)$wqpH?Ql)2mtWeNwtjEVZne~x%hx%OrAn`n9mrTZQlh5Y zFAq7%LHl*Ld3V;efXC&9!q#!fC{!m}50#$3;NLP0YBoyUSmSDq7f-9mr$;qdl8L8s z+Xy}o21rEm8oQQ4{|DBrd|aYGtX;xso-`?^M${b==!rlVjdkU!{Zv4h>D*^zOP|M3 zAc|Q^{H+ZLK!Qj1k|ds-D^cFS2}vg(EJ`)f%3#BY&?y5kJPGk;c?3GDh`PdWGZ*(0 zj14w20M5<=HSt5m`9Rcdw!6W&KW^m+V;g)|khx3YOA)q^^iOn^05RyCK~}yfVbX;D zf-oqdd(3~CjwJK#zh`%Ne%g_Y<~=O+SUj9==QL-jkc~{4{?gt9C`J}Uh4;{R@*t>U z@<_eeAN#u^O%LZ=$e6D27+Iy%b?e4i085bHbCj&t@Aa_pk<|lxxj!o(SGf|om-6_G zX52Tn6l@vi#T?)Sa>B`*j?f`~b*KP^D1RP~OsAv1Jqd&N}JkCf@3+a#3OGe4# z4`y(KgsJ#yPPk&!ukd1VNyz5W;Vo{g*uVr;$?I zhKv5yn#fkG6`HbAfLnIr(5*(D`Rjb<>kZ#VKS3#V%IP-P#s}x$nZf;PxVt!u;kc}0 z`#W7)CJq(O5O>_ZwP;vZY1*v300*u{!cNLg3h#DB(l}n#TjZmbbcoHGSnfE${bh@u zB%E12gq&<1ry#Gv4`_y!I_C%4BB`W9vgh-zMWpv``o9r#>Ur??h*g4O|%VK`e{}s!x~h ziLgs$#*)Tf-i=Tt7U)CpRcT@2`7so-1hXbP*L(@s#h+=EL-1&KAxRYOCusNxfl&Q^ zDCFx`$+^>$(NUwzvDEe!YerLVj4X4l``t>qxmL`6XUjF(H-j@xY8@3PFD zK;6Q4$QjxwRjB9e?*33fCY=N2ZlP;yVL>lora8RF99R0{iI}4|_=ou_l4R!MRJ$q8 zRicePb5gO)o_6iDzN4~-IX#Mx-*`?Dc0Nqgo!&hJo( z=bT;noN9N5^;f01E7*!Vr#UMf`JF6TNAVRF*)orpb5TsHSSO=zfVfW#s*kOv=PCt9%2%NWgB`E2#re8dhP=!uTUC}Qs+L~(MCi`UWKI(ZP=VBJ8Uo z`E`-5^+dhEi>{C07EMfOOENnZoTHh)@xcWiAmreDBf}v}M%onWVH9B#3*VG;a=~48 zsw^j@Udd>3^fA#B@PhhGQyQyT_GnmZhq{|ceX*Z}bA$Rxk|QN~`{fh~kHNmoFZeHl zxj2B62_k;y-hY8=mHN_}3b+iLL2W*L?evw$(43(D?izyz*QHva-HT(mA&Pp z6v16HrNrzZrFK}0oyVvnne195PbkWhyeA=;xC8R3JqsV#F5XTzsA9>C->WT%K3dPE zzPo%xEaB?;R|V6v`ri4x`H1QH;Lu~i4DsGt4o9)qOVgB>@5W_dKp>y7B8dzNQU;hd z3-Z)t5-JGaVxQ)Byq~nqgPY7HeFTVxc<@A#9!QYr@$9&pC7$H51#Pg?R728vrCSxQ z-8+!ax8SgkC_O)Qz|L>wm^X zvasRB3;_!+v#`uILQT>vbzAYua=s`}(@hH{S*8E7~kCODTnme>G<9B5gLq$N?;;~^SG_I_g1Endf53xU$dN<4Q4xHR>@~bNxl*D$0E|d-+I7DKbu7VmVYTG z;!mOXek4C{J9bcMJj?lxwX;<9fu<{KGGKD^g}});9lkBwJ>v3s;hw~YE~cJRma1L~ zkJSJgSC1W6k9qmg0rJ*eh2m0A{}Z;qo6XmCjoA%~q)>VSPSUKpd0oOI#|X0%Qq3&M zs1;`#m&43{UDI;ua9K{yT`8;t2Ero1<+P$(GxzKbI6nc&Ks;21->3NFW}suFkoyk` z^e++pA*Vry8SMeNG<%?$rcEAnz+iTFp@C><5T9KER`c2Cu(UVVQh}*zFM9d+>x0jP ztZ}(BGope&|9kU%7S84%A;1~!Uph&H`Sz#P*S(LUs}~GFwb9aOzRMYf1>&u z@k3P`a48S^c#i(-<;N#`|K>=`=P?*dA zP_YtA%vHzSj`-k#n2-=xxS(h9(CgDDBVpT0O@)EH4<^lZkp@|@a@bxC}#ffw+ z95?2a(yhWRHV<#)%{vSPxIZv$i8;bA)iKU}A}9~%68 z@Dd(Mb^}qEx9=3FgHi|nwG%b)ImoNw$X!aWdmh`pYwGkmJ-j~(sNsA3?C9@-q?ulh zxfV0Nme+4NxUByDSam;XtG)grDXAlX8jfo)&`Z(bE!XuV-MlK`fI>V{;X+DoDnsqF zNE0`jKK*dM9~t}s{J409x}zZsy{YU>>uhLveU{b}t5^ZpHD`kLIX^kOV3&BM&>YOrw zVvmM`5N50P>M=*YCR`Bg|7?qEQ&5&_KRrH{!ku3FN%Tlr@m{Y>ikQcZjp$^)nvw7? zQ0z)vt1fg;xEqe#^Kv=+edv#!5s%e74uVzPnNC~KL8lviEQd|?4|4U3?sB)A#~H>Z z!dRB2r2hFmW~`M?a6Myj3P*UUh`$lo!{~CFAeC7 zk39axJkhIpzc8JKj_w)VXBGlGu|2|u#`mqZMjXy|kzPNKQmlNE_jxr@b@hki?#ZE_ zlao{yh@Eg|GbP+Iu9EYRPJk75!E8nXs@Qt`#p-r+14@HtYC-gzhvx(oFhSz;H#I7r32$XWyI8y&~)AF+JK}*Xdn7e>}Vs5suja!Xn zkPB^^;UKs!+J{dh_7J={8*RT^9nqiN841;#v zja!*lYJvtHX3A>Y`5A~kmi3~+GSmKEv4X^`=Wj5up}T=p`~%V?HS`Ky*?Gq~A$T*U zjV~5O=!w}k%vOD@hUKx(6`0KY9cN^d#_KBKzMq7lC)VGb0>Hj1k>8Q%+D=r|rYCtM zt{C@%NeBx}4sty38DR>5|eJDvem+OkIh3%;)k!GrmRS#3&KZkrt6KUt7XfbPV0Wt=J$TfN>_y8GM;o+zxN61cFz7= z$xi9me2Q~=%nZ9+^DSFt%l$I((y7BP_t!_)EXq$76s%f}ac)~pfw&*Ru0`ERs)v3M zrdlbCFaZCIIK`Y?eqN+lF3}53IvHBOVC;_v)GK8jL>uCs9cnOOgcD?#cepuPS26?> zEuT}oC?CH=&3^e=&}RW!f=CFz>AmKy7%jKlMo&V=k=3T&MIY~5eGpOcA`J~qX%Bj| zv8*J*N6Sfi`@brDr#G$BaVF{MahniQX1r0IX+b4R{^y3TR1ubm#t{W z>-~xa#q{K8AovSrVX^PHMC1A{0bi2Mx)bi-hnmrYAv=bv8a^YbL4e~q*;UWQq5Hdm z;keFXEqt;M6r8w;Ia8)Rqim!Ktq^rp4;6vn;XLhfy^7tc<*~2b6I^i z(xfqk7nt++hf*a&1G9b_K4&lgq2G6Uw0d)st*oHnsxnDU80>_S-EV0_Z6F=R_aIXm z961zaa54P0LEU=TK|{l6GUkR`{od5|N2X5WX>2Eg|EyjYNh6c{NGz>Lu3I}vIGL(M zg`w??hLn+#kI}pa#~H|d7DmtWc023fR7l9P{@3S?`Mc3_^^r!L^oNY+9p<)&Xi6@$ z%U|Q`kx~UG+@Ex!zu&9@R*`+V7!350=(TF@iG;BNEgG1{F8IxkFRVJ&N=jtbE9#H{ z2F^|V%O0FQE!D4$>4*%kPl-nIIi`!|{(=wGnlbC3f8BmEom&#R9)}OWCW4i{{(p@G zs&FL$VY<%`eo=iEzl{HN5pN~HWe+-G(?b-Z?IN`n%?Ga4f@P-;VIXVTbA4V1;y){2^wzhl%N-2~028B$BWG!))yX zHOmsx*obazLr!Q-5ifU+Taf3piBzjr4IL``Gb6u{Ou!AOGpHocM+E9&tI>v5>n>!_ z%PCdw|4Au#rV^Dc5InL)J!3Xy3$6R6Oeys;g$>3q8i;GwXDa)+Bwg=p;EoE#{3Wbd zYsbd~2mg$XkKcH;>vGX|GM&o#F+xsEUi01prqIF%E%`%q%<=dQpu?6O{Lrt5iZJPg zA>G;E*l#E>ON=lgEwFD3rNCb>cgFc-UVwYnK8~RduwM;37||77Y%;Z z^Tw?u>5rH(?@!pQCDuhk-hV_vO}jW~3oUgbC%h^_8OtN`{NrO_!FBAjIy8=#otqbT zA*i^5hp*@Rss!&Ecn1-CFJP(X;g0H?oJ)R8CHWm`&lx3>F=22$kX&aI1z3u#lknA@ z)gRm}nbp&`bfCnm&kZ(FZEleFdFYg$O=0={HFI2+U9bziixYdc)=peYSQXt@3#>^3 zt3;O7a&v+=jQyjs8ChOeH~OdG*1a>nt`8h65&!a8;RXP_Y~Wv_}j=TdY7O48r4T19cOJ$nK}mJ#GW-HXx>UP zTN~2IK8thor>rh90z>q)4otE~a`nEc+Wk`{e;eQ$fT%O5pp#FO9>G$r8k$8t{2&VI zL~!Yh4yi(Rnpw(tN}W_v`RT`u&c8j4o$a`4&1I&|8C}E}-iVvkD`bne?~I~W>(C`k zOOwz-xo%7y-t~a0$ys?YR^=66+sRQJTv7m})p*<83FNf-%gXkMKsdN7&D`%0Xn`uJ z?BHL{YPcB)f|ZQ!*Y5DoRml*C6iUEIMxAP`XCz72T_)4IaE0Y_tNLF$S)r*Oq)KQE$Y%{1iC+6;sl;qU4V`&;=^TgGJjqE(tUu zN2dKH>yMOMY2fy1^g}uI(bff-E&B0yF+PWIrm4Qxe$W7BWftNlKwi|==7sBR*aF^ z?7Dq4noe1cI4AKnGlKh!p^^WC!T#dsHTfBX*ESMU`~C8RjAAlCp8$R<^7qp1Xm+f2 zQ<#_<^^uC@$1e~Dx@3;7ySW|qz3B{gh9~~RnvW2zdh1Zs4)^{q(U>I0m+49-K!t2yuN4I*2E&SAsr$@3`{ns9yq5Cc9g*f& zZfj;*(z8W8XJ>ve7by*6!sfh!Kjq^KDyb@4nxdKRNT6Nb#t&KE!Ig}w7C44U=KQk) zT-y@-!b$^{XHwVN`b4Qg&`eBwH{!vgw1$bQv~xgO55 z*Vo~5Z!Z_>Z7%UceZ$5|FVQ-x0ByS(tzYcvUvx^Px*P<1UCvv$ni7-A2Qj8RAfZBr z%4dgvJAJxOxK|A}abHr+-#jy663H;+@&BfyK*AelhLf0p;8K&iDjXfCm&uI_kxy&Ury_5nRtRwL$D z2Kp?mp++Zysae|m$;s0bYk<(mzfykWrgc9EC#&4!7#x4cwyJhcN3qs?o&6D}X{Ohg zuv}8BbXQTLHsTk)X`CvIm1g2uHc$aMw_KA4HbjiN=s2f;`Ups6&P=x#k=`}-Z$VGq z!p?5uH3qUJmw)>~i1}8AR&KGCxFl2^`#5Z8I_BrUuFFcVVCh(75rccgmNgNBmxn0~ zQyTqL_7p?o4s|JcN|DdYcP6Gg@WLp5X&AF(^;2Fw!4`-kX{_oN9m&eqRWJUuqdfH<_)WnpXzc`rWeO2C=%98}l*H%2gAT#|LL$x3m_gq3@ z(CJFE3ePIy!T#Rqf+pURV|lACA)D%7O0>4sJSp`qBKB+SsHpDIrHgER+L!*D*-hSu z;Yj_F7-vP^d%qb=lNEK119iy+r;LF?er-;3SgCJJzG~*zanZ$~C}a4GQ01o{CobFj zr-S%~_{(KN9(%$*2!LJ=Gik8~Fx-NnSob8Z%flJh5w|z^SV<{}Z0uJCvyfPwKNyh&n>p%v&3bXRE)Y zB1>*&U)CEgz+s2eavQ*6xlc>Ogk!L>WYt05ZWtqZG|v~K@BOLQEW1bR2l#3REDYp#oOc<@DC3%An@DDboq-y9d-DMmuouoe?C)U?%>Bk;58?&j+tjL$4 zSn|O(9(y}XXbXZN6Bn zv_Mj8a9M#@CEuqM09)TD3{;UM=a=M*u%*=pYZEk+?(r9V^ihS8zCi$^>NA&l3e!tmy-$|{&Hq+0Vx=6($r>=3f z#aZ|BU%pom1@>YvkpI>`w3v&TwSsB-B&jGei{XwwctTDI0Z~!KeZL5H{rMwYP@Wr+ zd(D=HETO<|N|l|?889<5l5uiQ-d$j!GB?Na5M^`K1kom*406Io#q(EIlF7B^lgo#p zWxz(ted!SchHj^*9O8$WpljSxFn3&XSNdZk$@@=bUzcG+UX3E6;vdn*i=vjH2AUTV|ko$XIs?Fm3 z>Sf0wY*sEVUeahwIwmW5vQUO)&8b=Vnj#u>lUTJrpmC5fyAyj0!EjbVr#k%~FHbIy zGn3gtta=7}aWAQtQ3_Z!4~lXG+{yjWOz&${?`~@$x^CwWxYV?^Gpu5Ra(%}>y}x|` z(h|09H@2#162n#SO(fLTvA=9cSC{3?xMkR}dw?e5@R6ra0K#ip$SGBP8xc-RH8w0s z7G_~p-I|1!7N`l|NOuWK`Q#1iE>fH+N)r?f&iS1B*G_qI@AUmG`l&Pjltq=92>Sc#;J=HrefO{2MhEDmmES$hi84MY%C1 z-#6-|RWZ*8xotj|sTOw}3CSOB?*PZ9sBhuZspf;uUozmR6o z&?+b?&Imoo9<9)8Nvzebb&Zn8I>O0d-=vtoXcN|YP^QvV5nqvRQtJE&#(z3`(lFTe zC$XKSRqeRH4da)=(O9ICIxx-n_aUb9#x>`P@`y}UMFLANaz}0minwRFns5eW8szHe z;C-uRtdIUdpBvI-Io1^Ncu@qELT&=)%%%LN4fl>B7$E(;S9SDN-7R z$Xt`g`kyby!pg!WbD`njjAB4U;u*-$kss;HhRcqFtI7#|KuDUpf@KTkTOQ#_H*ZQr z$yPrs-=SYA_YiygA{-3^MJe!)fgD`lf57Wjp*3wpmmZ}Y!Ct8&G!D&Ie8Hv^_nl&A z_!m&%7SiekVW~9j{Yx{HXL}9uygx2W|-PsA19b6|W*S%&jV|u)LzsPzT zO=Ni8hK2h1el0`g_2kyPiB>P*JpL*7?k`V1_fn(m=yJbn(wlGG;feWQ0OEasFitbH z{$j`kdT57}7=WnH2r1%o%2)NTixT%fy|BGdD{da(z?lxEIDL9jcC;CYh@cw_4la^p zj4h!Ip|&;6n9C)CNp%b`z_UfEUm)mwl?L52!+A%q5^sKj_rJ53PaD=mWnG<*@wlzo=pZ)V7 zub9*d3Gl}PKMgtU955?DLzf~EBtsWvk@iw_K+JA0ZtPJ=sM6eE7ydTk4;`Q9^C0b+%|y9i3USO^o}8j=oaQ zLXVz*rC9$;fr4I|)Zp8;mJ%k{lO)CnnrVzl>$?aL6t`yAnK@@FxP1KfIo%!^{3glf zd8vOZKV-Z_sBwR~3VIocEcZSV*S_;O%4<_3#3G*)p_>cB`1NT;Or%q~c+;w8*+qJO zB{#Q}t^(Q{A9Gdh=$z@t5^1d{A{c#5cG+e_&Fx^vrA^XGT(mhrJuPbM-q6C@FbJfq zhI^H!IgshiOP%S!v@;l8v`G^`3P}ag01K4*cI@cr=-iJmtdhKac;aUHb8rW3V2uBIB%-F@kzS_U1x{MlY z&3#vh3Mtvy zCy}F9HAR(`Q5fVBw!Nrawl8N*4{^Y=tzaVr`-9e|uAA;>ST5p^t=lUirHsHp0h^BQ zEQG4OF!vFD%&x*eA*Vw~y->YM)t^HReD{2bV0c{446nqM$x zsjQ?GV7;rtw*i?63Kn;&KaO(KdAw&nBTJ1UJ-4@1w8>*&hDd%Fj4ta z(5IYWF&SW}6G?F*^{KHIuMm4=a)Ev`9sY6cUW=Ss$Y_0=!IZ%kJ!h2`QvIl{2wN!M zb*u?h2S3SBo}B(>Bhq9fID4GdX15WfmdTW|)A;tD0mD2<9n#v$3;!7p%6IbMgd-NS z?cI;_vGz?*sJ?&m*nnhWW556v!!nMq3%IKygvJ zaT~3?*FQD}^ZGf(8lx?RW@gSMykUi=XycOCJPtiH{R+ag_@KBk)rY(FOKL5)p25^?&-;}XQIRhu$Rbu0LD%G*f_~TA(%td zsw^=ggYpn<%umhQEt)em5wQ4>2KeQcaG()PmN9g)(T-|&jV0kIAZ7nujtUOZ1& z<#w(}!xEKUs#PTpV3QkKl)Jb!YTMqgG&U~gCl2E7R%x;RqTL{?>kvA$w~sYAYe!i` zQWBwQ-TlO4-MRjIqV^lqUo7jOX=;b-fqsn_ZkEe*=IXQHqC5AnN0IH@V4+vPz0u^0 zu4y3k(ds>XmN5eI(h@LgDyH@Y7HUcdH|Ba%b)wS>7ir1QFU-Html0c=nB>uas9kEL z>;>cY#hw~W7)+5RgBd-@8i#ahjJz_mX-|k3*g5s||7C8pPk ztx1TpNftU?(;(P3G`*32emvsnK%>>E47~X46gJNI8Q=lo68=?^hz3&TN1gbvJReX_ zU@@AU}K7YN@mM0SF=1nLZ zx5H_oe2=4sgP(Fd_Paw@v=GgWtLsr_k}attfoiMMQytF-&AmyY5 z4^~}BA8zb_i;?izuekr`7`tO63F)nZT#yeAMtz{2bv|zXm^}1@f`X!S6jygNHgGiI zHL^E>d_b|WuyQc6@G!D)tFUtNva#}VK)!PDvap=BIa2+$5J5< z-2bg$VPwQ>WMk`QZ((Nc2qpS|SEMvXw4|&&Jlv!}6BkPpM@L2l1Eb#t_Qs^5HdeL< z)^1N~Zd{N#$o_8*V-p7>dkb4f3ma=F2PH`WDM(zDla+^)laY;yRjp!K5@JmCf5uh@ z)=s7dMvhMQCiYMeLsD}`M_XQIW)~M1CJPfICL?n~A1E1!2iN~RY>Z7T|6kMpO-d?e zVrt-I=}7wDYDPAeHcs}W4z>nHCZvp{|J{L@GOz?P1@3=SI9iyR@)}s$nj1hlSsOSw z*ce$rw$wO+2Br>Tg7!ZX6Ki7&18XQN2SXJnFGt7__Wy=#&21cQtV|s3EsUTbv)7S= qHX!3)|2J-L1RteDe|9=3IAByS# literal 0 HcmV?d00001 diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html new file mode 100644 index 00000000..056cff03 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html @@ -0,0 +1,156 @@ + + + + +Trick Memory Allocations + + + + + +
+
+
+
+ + + + + diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/TrickLogoSmall.png b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/TrickLogoSmall.png new file mode 100644 index 0000000000000000000000000000000000000000..d6712b42f649242d1260eaa2ef5c7dfb9f2764f0 GIT binary patch literal 28273 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 RLl1}p$Vn+nR*9R0{13%sC>;O* literal 0 HcmV?d00001 diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/asset-manifest.json b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/asset-manifest.json new file mode 100644 index 00000000..33338ad4 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/asset-manifest.json @@ -0,0 +1,18 @@ +{ + "files": { + "main.css": "./static/css/main.a82b4685.chunk.css", + "main.js": "./static/js/main.593ee672.chunk.js", + "main.js.map": "./static/js/main.593ee672.chunk.js.map", + "runtime~main.js": "./static/js/runtime~main.d653cc00.js", + "runtime~main.js.map": "./static/js/runtime~main.d653cc00.js.map", + "static/css/2.335e4b44.chunk.css": "./static/css/2.335e4b44.chunk.css", + "static/js/2.fd0d41cf.chunk.js": "./static/js/2.fd0d41cf.chunk.js", + "static/js/2.fd0d41cf.chunk.js.map": "./static/js/2.fd0d41cf.chunk.js.map", + "index.html": "./index.html", + "precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js": "./precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js", + "service-worker.js": "./service-worker.js", + "static/css/2.335e4b44.chunk.css.map": "./static/css/2.335e4b44.chunk.css.map", + "static/css/main.a82b4685.chunk.css.map": "./static/css/main.a82b4685.chunk.css.map", + "static/media/index.css": "./static/media/roboto-latin-900italic.ebf6d164.woff2" + } +} \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/index.html b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/index.html new file mode 100644 index 00000000..d4711813 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/index.html @@ -0,0 +1 @@ +React App
\ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/manifest.json b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/manifest.json new file mode 100644 index 00000000..1f2f141f --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js new file mode 100644 index 00000000..b4b8c165 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js @@ -0,0 +1,122 @@ +self.__precacheManifest = (self.__precacheManifest || []).concat([ + { + "revision": "577c8ea84fd85c60dc5f5c579560e83d", + "url": "./index.html" + }, + { + "revision": "6c79f9b2aedb002d302c", + "url": "./static/css/2.335e4b44.chunk.css" + }, + { + "revision": "9b46f0f8321ce396aba6", + "url": "./static/css/main.a82b4685.chunk.css" + }, + { + "revision": "6c79f9b2aedb002d302c", + "url": "./static/js/2.fd0d41cf.chunk.js" + }, + { + "revision": "9b46f0f8321ce396aba6", + "url": "./static/js/main.593ee672.chunk.js" + }, + { + "revision": "8c97409f0ee389fe75da", + "url": "./static/js/runtime~main.d653cc00.js" + }, + { + "revision": "5cb7edfceb233100075dc9a1e12e8da3", + "url": "./static/media/roboto-latin-100.5cb7edfc.woff" + }, + { + "revision": "7370c3679472e9560965ff48a4399d0b", + "url": "./static/media/roboto-latin-100.7370c367.woff2" + }, + { + "revision": "f8b1df51ba843179fa1cc9b53d58127a", + "url": "./static/media/roboto-latin-100italic.f8b1df51.woff2" + }, + { + "revision": "f9e8e590b4e0f1ff83469bb2a55b8488", + "url": "./static/media/roboto-latin-100italic.f9e8e590.woff" + }, + { + "revision": "b00849e00f4c2331cddd8ffb44a6720b", + "url": "./static/media/roboto-latin-300.b00849e0.woff" + }, + { + "revision": "ef7c6637c68f269a882e73bcb57a7f6a", + "url": "./static/media/roboto-latin-300.ef7c6637.woff2" + }, + { + "revision": "14286f3ba79c6627433572dfa925202e", + "url": "./static/media/roboto-latin-300italic.14286f3b.woff2" + }, + { + "revision": "4df32891a5f2f98a363314f595482e08", + "url": "./static/media/roboto-latin-300italic.4df32891.woff" + }, + { + "revision": "479970ffb74f2117317f9d24d9e317fe", + "url": "./static/media/roboto-latin-400.479970ff.woff2" + }, + { + "revision": "60fa3c0614b8fb2f394fa29944c21540", + "url": "./static/media/roboto-latin-400.60fa3c06.woff" + }, + { + "revision": "51521a2a8da71e50d871ac6fd2187e87", + "url": "./static/media/roboto-latin-400italic.51521a2a.woff2" + }, + { + "revision": "fe65b8335ee19dd944289f9ed3178c78", + "url": "./static/media/roboto-latin-400italic.fe65b833.woff" + }, + { + "revision": "020c97dc8e0463259c2f9df929bb0c69", + "url": "./static/media/roboto-latin-500.020c97dc.woff2" + }, + { + "revision": "87284894879f5b1c229cb49c8ff6decc", + "url": "./static/media/roboto-latin-500.87284894.woff" + }, + { + "revision": "288ad9c6e8b43cf02443a1f499bdf67e", + "url": "./static/media/roboto-latin-500italic.288ad9c6.woff" + }, + { + "revision": "db4a2a231f52e497c0191e8966b0ee58", + "url": "./static/media/roboto-latin-500italic.db4a2a23.woff2" + }, + { + "revision": "2735a3a69b509faf3577afd25bdf552e", + "url": "./static/media/roboto-latin-700.2735a3a6.woff2" + }, + { + "revision": "adcde98f1d584de52060ad7b16373da3", + "url": "./static/media/roboto-latin-700.adcde98f.woff" + }, + { + "revision": "81f57861ed4ac74741f5671e1dff2fd9", + "url": "./static/media/roboto-latin-700italic.81f57861.woff" + }, + { + "revision": "da0e717829e033a69dec97f1e155ae42", + "url": "./static/media/roboto-latin-700italic.da0e7178.woff2" + }, + { + "revision": "9b3766ef4a402ad3fdeef7501a456512", + "url": "./static/media/roboto-latin-900.9b3766ef.woff2" + }, + { + "revision": "bb1e4dc6333675d11ada2e857e7f95d7", + "url": "./static/media/roboto-latin-900.bb1e4dc6.woff" + }, + { + "revision": "28f9151055c950874d2c6803a39b425b", + "url": "./static/media/roboto-latin-900italic.28f91510.woff" + }, + { + "revision": "ebf6d1640ccddb99fb49f73c052c55a8", + "url": "./static/media/roboto-latin-900italic.ebf6d164.woff2" + } +]); \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/service-worker.js b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/service-worker.js new file mode 100644 index 00000000..b5113d07 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/service-worker.js @@ -0,0 +1,39 @@ +/** + * Welcome to your Workbox-powered service worker! + * + * You'll need to register this file in your web app and you should + * disable HTTP caching for this file too. + * See https://goo.gl/nhQhGp + * + * The rest of the code is auto-generated. Please don't update this file + * directly; instead, make changes to your Workbox build configuration + * and re-run your build process. + * See https://goo.gl/2aRDsh + */ + +importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); + +importScripts( + "./precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js" +); + +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +workbox.core.clientsClaim(); + +/** + * The workboxSW.precacheAndRoute() method efficiently caches and responds to + * requests for URLs in the manifest. + * See https://goo.gl/S9QRab + */ +self.__precacheManifest = [].concat(self.__precacheManifest || []); +workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); + +workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), { + + blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/], +}); diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css new file mode 100644 index 00000000..822243b2 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css @@ -0,0 +1,2 @@ +@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:100;src:local("Roboto Thin "),local("Roboto-Thin"),url(../../static/media/roboto-latin-100.7370c367.woff2) format("woff2"),url(../../static/media/roboto-latin-100.5cb7edfc.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:100;src:local("Roboto Thin italic"),local("Roboto-Thinitalic"),url(../../static/media/roboto-latin-100italic.f8b1df51.woff2) format("woff2"),url(../../static/media/roboto-latin-100italic.f9e8e590.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:300;src:local("Roboto Light "),local("Roboto-Light"),url(../../static/media/roboto-latin-300.ef7c6637.woff2) format("woff2"),url(../../static/media/roboto-latin-300.b00849e0.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:300;src:local("Roboto Light italic"),local("Roboto-Lightitalic"),url(../../static/media/roboto-latin-300italic.14286f3b.woff2) format("woff2"),url(../../static/media/roboto-latin-300italic.4df32891.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Regular "),local("Roboto-Regular"),url(../../static/media/roboto-latin-400.479970ff.woff2) format("woff2"),url(../../static/media/roboto-latin-400.60fa3c06.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:400;src:local("Roboto Regular italic"),local("Roboto-Regularitalic"),url(../../static/media/roboto-latin-400italic.51521a2a.woff2) format("woff2"),url(../../static/media/roboto-latin-400italic.fe65b833.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:500;src:local("Roboto Medium "),local("Roboto-Medium"),url(../../static/media/roboto-latin-500.020c97dc.woff2) format("woff2"),url(../../static/media/roboto-latin-500.87284894.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:500;src:local("Roboto Medium italic"),local("Roboto-Mediumitalic"),url(../../static/media/roboto-latin-500italic.db4a2a23.woff2) format("woff2"),url(../../static/media/roboto-latin-500italic.288ad9c6.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:700;src:local("Roboto Bold "),local("Roboto-Bold"),url(../../static/media/roboto-latin-700.2735a3a6.woff2) format("woff2"),url(../../static/media/roboto-latin-700.adcde98f.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:700;src:local("Roboto Bold italic"),local("Roboto-Bolditalic"),url(../../static/media/roboto-latin-700italic.da0e7178.woff2) format("woff2"),url(../../static/media/roboto-latin-700italic.81f57861.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:900;src:local("Roboto Black "),local("Roboto-Black"),url(../../static/media/roboto-latin-900.9b3766ef.woff2) format("woff2"),url(../../static/media/roboto-latin-900.bb1e4dc6.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:900;src:local("Roboto Black italic"),local("Roboto-Blackitalic"),url(../../static/media/roboto-latin-900italic.ebf6d164.woff2) format("woff2"),url(../../static/media/roboto-latin-900italic.28f91510.woff) format("woff")} +/*# sourceMappingURL=2.335e4b44.chunk.css.map */ \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css.map b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css.map new file mode 100644 index 00000000..30d6dadf --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/2.335e4b44.chunk.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.css"],"names":[],"mappings":"AACA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,4LAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,oNAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8LAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sNAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,kMAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,0NAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,gMAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,wNAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,4LAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,oNAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,8LAKF,CAGA,WACE,kBAAqB,CACrB,iBAAkB,CAClB,iBAAkB,CAClB,eAAgB,CAChB,sNAKF","file":"2.335e4b44.chunk.css","sourcesContent":["/* roboto-100normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 100;\n src:\n local('Roboto Thin '),\n local('Roboto-Thin'),\n url('./files/roboto-latin-100.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-100.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-100italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 100;\n src:\n local('Roboto Thin italic'),\n local('Roboto-Thinitalic'),\n url('./files/roboto-latin-100italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-100italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-300normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 300;\n src:\n local('Roboto Light '),\n local('Roboto-Light'),\n url('./files/roboto-latin-300.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-300.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-300italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 300;\n src:\n local('Roboto Light italic'),\n local('Roboto-Lightitalic'),\n url('./files/roboto-latin-300italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-300italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-400normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 400;\n src:\n local('Roboto Regular '),\n local('Roboto-Regular'),\n url('./files/roboto-latin-400.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-400.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-400italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 400;\n src:\n local('Roboto Regular italic'),\n local('Roboto-Regularitalic'),\n url('./files/roboto-latin-400italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-400italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-500normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 500;\n src:\n local('Roboto Medium '),\n local('Roboto-Medium'),\n url('./files/roboto-latin-500.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-500.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-500italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 500;\n src:\n local('Roboto Medium italic'),\n local('Roboto-Mediumitalic'),\n url('./files/roboto-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-500italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-700normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 700;\n src:\n local('Roboto Bold '),\n local('Roboto-Bold'),\n url('./files/roboto-latin-700.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-700.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-700italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 700;\n src:\n local('Roboto Bold italic'),\n local('Roboto-Bolditalic'),\n url('./files/roboto-latin-700italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-700italic.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-900normal - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: normal;\n font-display: swap;\n font-weight: 900;\n src:\n local('Roboto Black '),\n local('Roboto-Black'),\n url('./files/roboto-latin-900.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-900.woff') format('woff'); /* Modern Browsers */\n}\n\n/* roboto-900italic - latin */\n@font-face {\n font-family: 'Roboto';\n font-style: italic;\n font-display: swap;\n font-weight: 900;\n src:\n local('Roboto Black italic'),\n local('Roboto-Blackitalic'),\n url('./files/roboto-latin-900italic.woff2') format('woff2'), /* Super Modern Browsers */\n url('./files/roboto-latin-900italic.woff') format('woff'); /* Modern Browsers */\n}\n\n"]} \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css new file mode 100644 index 00000000..93b61525 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css @@ -0,0 +1,2 @@ +body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace} +/*# sourceMappingURL=main.a82b4685.chunk.css.map */ \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css.map b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css.map new file mode 100644 index 00000000..dea0a0c4 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/css/main.a82b4685.chunk.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["index.css"],"names":[],"mappings":"AACA,KACE,QAAS,CACT,mIAEY,CACZ,kCAAmC,CACnC,iCACF,CAEA,KACE,uEAEF","file":"main.a82b4685.chunk.css","sourcesContent":["\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n"]} \ No newline at end of file diff --git a/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/2.fd0d41cf.chunk.js b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/2.fd0d41cf.chunk.js new file mode 100644 index 00000000..b7bf1f10 --- /dev/null +++ b/trick_sims/Cannon/SIM_cannon_webserver/www/apps/react/static/js/2.fd0d41cf.chunk.js @@ -0,0 +1,2 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],[function(e,t,n){"use strict";e.exports=n(87)},function(e,t,n){"use strict";function r(){return(r=Object.assign||function(e){for(var t=1;t=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}},function(e,t,n){"use strict";function r(e){var t,n,o="";if(e)if("object"===typeof e)if(e.push)for(t=0;t1&&void 0!==arguments[1]?arguments[1]:{};return function(n){var a=t.defaultTheme,l=t.withTheme,f=void 0!==l&&l,p=t.name,h=Object(o.a)(t,["defaultTheme","withTheme","name"]),m=p,v=Object(s.a)(e,Object(r.a)({defaultTheme:a,Component:n,name:p||n.displayName,classNamePrefix:m},h)),b=i.a.forwardRef(function(e,t){e.classes;var l,u=e.innerRef,s=Object(o.a)(e,["classes","innerRef"]),h=v(e),m=s;return("string"===typeof p||f)&&(l=Object(d.a)()||a,p&&(m=c({theme:l,name:p,props:s})),f&&!m.theme&&(m.theme=l)),i.a.createElement(n,Object(r.a)({ref:u||t,classes:h},m))});return u()(b,n),b}},p=n(25);t.a=function(e,t){return f(e,Object(r.a)({defaultTheme:p.a},t))}},function(e,t,n){e.exports=n(96)()},function(e,t,n){"use strict";n.d(t,"a",function(){return a}),n.d(t,"b",function(){return i}),n.d(t,"c",function(){return l});var r=n(0),o=n.n(r);function a(e,t){return o.a.isValidElement(e)&&-1!==t.indexOf(e.type.muiName)}function i(e,t){"function"===typeof e?e(t):e&&(e.current=t)}function l(e,t){return o.a.useMemo(function(){return null==e&&null==t?null:function(n){i(e,n),i(t,n)}},[e,t])}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return o});n(10);function r(e){return e.charAt(0).toUpperCase()+e.slice(1)}function o(){for(var e=arguments.length,t=new Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return en?n:e}function o(e){if(e.type)return e;if("#"===e.charAt(0))return o(function(e){e=e.substr(1);var t=new RegExp(".{1,".concat(e.length/3,"}"),"g"),n=e.match(t);return n&&1===n[0].length&&(n=n.map(function(e){return e+e})),n?"rgb(".concat(n.map(function(e){return parseInt(e,16)}).join(", "),")"):""}(e));var t=e.indexOf("("),n=e.substring(0,t);if(-1===["rgb","rgba","hsl","hsla"].indexOf(n))throw new Error(["Material-UI: unsupported `".concat(e,"` color."),"We support the following formats: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()."].join("\n"));var r=e.substring(t+1,e.length-1).split(",");return{type:n,values:r=r.map(function(e){return parseFloat(e)})}}function a(e){var t=e.type,n=e.values;return-1!==t.indexOf("rgb")?n=n.map(function(e,t){return t<3?parseInt(e,10):e}):-1!==t.indexOf("hsl")&&(n[1]="".concat(n[1],"%"),n[2]="".concat(n[2],"%")),"".concat(t,"(").concat(n.join(", "),")")}function i(e,t){var n=l(e),r=l(t);return(Math.max(n,r)+.05)/(Math.min(n,r)+.05)}function l(e){var t="hsl"===(e=o(e)).type?o(function(e){var t=(e=o(e)).values,n=t[0],r=t[1]/100,i=t[2]/100,l=r*Math.min(i,1-i),u=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(e+n/30)%12;return i-l*Math.max(Math.min(t-3,9-t,1),-1)},s="rgb",c=[Math.round(255*u(0)),Math.round(255*u(8)),Math.round(255*u(4))];return"hsla"===e.type&&(s+="a",c.push(t[3])),a({type:s,values:c})}(e)).values:e.values;return t=t.map(function(e){return(e/=255)<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4)}),Number((.2126*t[0]+.7152*t[1]+.0722*t[2]).toFixed(3))}function u(e,t){return e=o(e),t=r(t),"rgb"!==e.type&&"hsl"!==e.type||(e.type+="a"),e.values[3]=t,a(e)}function s(e,t){if(e=o(e),t=r(t),-1!==e.type.indexOf("hsl"))e.values[2]*=1-t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]*=1-t;return a(e)}function c(e,t){if(e=o(e),t=r(t),-1!==e.type.indexOf("hsl"))e.values[2]+=(100-e.values[2])*t;else if(-1!==e.type.indexOf("rgb"))for(var n=0;n<3;n+=1)e.values[n]+=(255-e.values[n])*t;return a(e)}},function(e,t,n){"use strict";n.r(t);var r=n(17),o=n.n(r),a=function(e,t){var n=o()({},e,t);for(var r in e){var a;e[r]&&"object"===typeof t[r]&&o()(n,((a={})[r]=o()(e[r],t[r]),a))}return n},i={breakpoints:[40,52,64].map(function(e){return e+"em"})},l=function(e){return"@media screen and (min-width: "+e+")"},u=function(e,t){return s(t,e,e)},s=function(e,t,n,r,o){for(t=t&&t.split?t.split("."):[t],r=0;r1&&u.forEach(function(n){var o;r[n]=e(((o={})[n]=t[n],o))}),r},d=function(e,t,n,r){var a={};return r.slice(0,e.length).forEach(function(r,i){var l,u=e[i],s=t(r,n);u?o()(a,((l={})[u]=o()({},a[u],s),l)):o()(a,s)}),a},f=function(e,t,n,r){var a={};for(var i in r){var u=e[i],s=t(r[i],n);if(u){var c,d=l(u);o()(a,((c={})[d]=o()({},a[d],s),c))}else o()(a,s)}return a},p=function(e){var t=e.properties,n=e.property,r=e.scale,o=e.transform,a=void 0===o?u:o,i=e.defaultScale;t=t||[n];var l=function(e,n){var r={},o=a(e,n);if(null!==o)return t.forEach(function(e){r[e]=o}),r};return l.scale=r,l.defaults=i,l},h=function(e){void 0===e&&(e={});var t={};return Object.keys(e).forEach(function(n){var r=e[n];t[n]=!0!==r?"function"!==typeof r?p(r):r:p({property:n,scale:n})}),c(t)},m=function(){for(var e={},t=arguments.length,n=new Array(t),r=0;r1?e:100*e+"%")}},height:{property:"height",scale:"sizes"},minWidth:{property:"minWidth",scale:"sizes"},minHeight:{property:"minHeight",scale:"sizes"},maxWidth:{property:"maxWidth",scale:"sizes"},maxHeight:{property:"maxHeight",scale:"sizes"},size:{properties:["width","height"],scale:"sizes"},overflow:!0,display:!0,verticalAlign:!0}),b=v,g={color:{property:"color",scale:"colors"},backgroundColor:{property:"backgroundColor",scale:"colors"},opacity:!0};g.bg=g.backgroundColor;var y=h(g),x=y,k=h({fontFamily:{property:"fontFamily",scale:"fonts"},fontSize:{property:"fontSize",scale:"fontSizes",defaultScale:[12,14,16,20,24,32,48,64,72]},fontWeight:{property:"fontWeight",scale:"fontWeights"},lineHeight:{property:"lineHeight",scale:"lineHeights"},letterSpacing:{property:"letterSpacing",scale:"letterSpacings"},textAlign:!0,fontStyle:!0}),w=k,O=h({alignItems:!0,alignContent:!0,justifyItems:!0,justifyContent:!0,flexWrap:!0,flexDirection:!0,flex:!0,flexGrow:!0,flexShrink:!0,flexBasis:!0,justifySelf:!0,alignSelf:!0,order:!0}),S=O,C={space:[0,4,8,16,32,64,128,256,512]},E=h({gridGap:{property:"gridGap",scale:"space",defaultScale:C.space},gridColumnGap:{property:"gridColumnGap",scale:"space",defaultScale:C.space},gridRowGap:{property:"gridRowGap",scale:"space",defaultScale:C.space},gridColumn:!0,gridRow:!0,gridAutoFlow:!0,gridAutoColumns:!0,gridAutoRows:!0,gridTemplateColumns:!0,gridTemplateRows:!0,gridTemplateAreas:!0,gridArea:!0}),j=E,T=h({border:{property:"border",scale:"borders"},borderWidth:{property:"borderWidth",scale:"borderWidths"},borderStyle:{property:"borderStyle",scale:"borderStyles"},borderColor:{property:"borderColor",scale:"colors"},borderRadius:{property:"borderRadius",scale:"radii"},borderTop:{property:"borderTop",scale:"borders"},borderRight:{property:"borderRight",scale:"borders"},borderBottom:{property:"borderBottom",scale:"borders"},borderLeft:{property:"borderLeft",scale:"borders"},borderX:{properties:["borderLeft","borderRight"],scale:"borders"},borderY:{properties:["borderTop","borderBottom"],scale:"borders"}}),P=T,R={background:!0,backgroundImage:!0,backgroundSize:!0,backgroundPosition:!0,backgroundRepeat:!0};R.bgImage=R.backgroundImage,R.bgSize=R.backgroundSize,R.bgPosition=R.backgroundPosition,R.bgRepeat=R.backgroundRepeat;var N=h(R),_=N,A={space:[0,4,8,16,32,64,128,256,512]},M=h({position:!0,zIndex:{property:"zIndex",scale:"zIndices"},top:{property:"top",scale:"space",defaultScale:A.space},right:{property:"right",scale:"space",defaultScale:A.space},bottom:{property:"bottom",scale:"space",defaultScale:A.space},left:{property:"left",scale:"space",defaultScale:A.space}}),I=M,z={space:[0,4,8,16,32,64,128,256,512]},D=function(e){return"number"===typeof e&&!isNaN(e)},L=function(e,t){if(!D(e))return s(t,e,e);var n=e<0,r=Math.abs(e),o=s(t,r,r);return D(o)?o*(n?-1:1):n?"-"+o:o},F={};F.margin={margin:{property:"margin",scale:"space",transform:L,defaultScale:z.space},marginTop:{property:"marginTop",scale:"space",transform:L,defaultScale:z.space},marginRight:{property:"marginRight",scale:"space",transform:L,defaultScale:z.space},marginBottom:{property:"marginBottom",scale:"space",transform:L,defaultScale:z.space},marginLeft:{property:"marginLeft",scale:"space",transform:L,defaultScale:z.space},marginX:{properties:["marginLeft","marginRight"],scale:"space",transform:L,defaultScale:z.space},marginY:{properties:["marginTop","marginBottom"],scale:"space",transform:L,defaultScale:z.space}},F.margin.m=F.margin.margin,F.margin.mt=F.margin.marginTop,F.margin.mr=F.margin.marginRight,F.margin.mb=F.margin.marginBottom,F.margin.ml=F.margin.marginLeft,F.margin.mx=F.margin.marginX,F.margin.my=F.margin.marginY,F.padding={padding:{property:"padding",scale:"space",defaultScale:z.space},paddingTop:{property:"paddingTop",scale:"space",defaultScale:z.space},paddingRight:{property:"paddingRight",scale:"space",defaultScale:z.space},paddingBottom:{property:"paddingBottom",scale:"space",defaultScale:z.space},paddingLeft:{property:"paddingLeft",scale:"space",defaultScale:z.space},paddingX:{properties:["paddingLeft","paddingRight"],scale:"space",defaultScale:z.space},paddingY:{properties:["paddingTop","paddingBottom"],scale:"space",defaultScale:z.space}},F.padding.p=F.padding.padding,F.padding.pt=F.padding.paddingTop,F.padding.pr=F.padding.paddingRight,F.padding.pb=F.padding.paddingBottom,F.padding.pl=F.padding.paddingLeft,F.padding.px=F.padding.paddingX,F.padding.py=F.padding.paddingY;var W=h(F.margin),B=h(F.padding),U=m(W,B),H=h({boxShadow:{property:"boxShadow",scale:"shadows"},textShadow:{property:"textShadow",scale:"shadows"}}),$=H,V=function(e){var t,n=e.scale,r=e.prop,o=void 0===r?"variant":r,a=e.key,i=function(e,t){return s(t,e,null)};i.scale=n||a;var l=((t={})[o]=i,t);return c(l)},q=V({key:"buttons"}),K=V({key:"textStyles",prop:"textStyle"}),X=V({key:"colorStyles",prop:"colors"});n.d(t,"width",function(){return G}),n.d(t,"height",function(){return Y}),n.d(t,"minWidth",function(){return Q}),n.d(t,"minHeight",function(){return J}),n.d(t,"maxWidth",function(){return Z}),n.d(t,"maxHeight",function(){return ee}),n.d(t,"size",function(){return te}),n.d(t,"verticalAlign",function(){return ne}),n.d(t,"display",function(){return re}),n.d(t,"overflow",function(){return oe}),n.d(t,"opacity",function(){return ae}),n.d(t,"fontSize",function(){return ie}),n.d(t,"fontFamily",function(){return le}),n.d(t,"fontWeight",function(){return ue}),n.d(t,"lineHeight",function(){return se}),n.d(t,"textAlign",function(){return ce}),n.d(t,"fontStyle",function(){return de}),n.d(t,"letterSpacing",function(){return fe}),n.d(t,"alignItems",function(){return pe}),n.d(t,"alignContent",function(){return he}),n.d(t,"justifyItems",function(){return me}),n.d(t,"justifyContent",function(){return ve}),n.d(t,"flexWrap",function(){return be}),n.d(t,"flexDirection",function(){return ge}),n.d(t,"flex",function(){return ye}),n.d(t,"flexGrow",function(){return xe}),n.d(t,"flexShrink",function(){return ke}),n.d(t,"flexBasis",function(){return we}),n.d(t,"justifySelf",function(){return Oe}),n.d(t,"alignSelf",function(){return Se}),n.d(t,"order",function(){return Ce}),n.d(t,"gridGap",function(){return Ee}),n.d(t,"gridColumnGap",function(){return je}),n.d(t,"gridRowGap",function(){return Te}),n.d(t,"gridColumn",function(){return Pe}),n.d(t,"gridRow",function(){return Re}),n.d(t,"gridAutoFlow",function(){return Ne}),n.d(t,"gridAutoColumns",function(){return _e}),n.d(t,"gridAutoRows",function(){return Ae}),n.d(t,"gridTemplateColumns",function(){return Me}),n.d(t,"gridTemplateRows",function(){return Ie}),n.d(t,"gridTemplateAreas",function(){return ze}),n.d(t,"gridArea",function(){return De}),n.d(t,"borderWidth",function(){return Le}),n.d(t,"borderStyle",function(){return Fe}),n.d(t,"borderColor",function(){return We}),n.d(t,"borderTop",function(){return Be}),n.d(t,"borderRight",function(){return Ue}),n.d(t,"borderBottom",function(){return He}),n.d(t,"borderLeft",function(){return $e}),n.d(t,"borderRadius",function(){return Ve}),n.d(t,"backgroundImage",function(){return qe}),n.d(t,"backgroundSize",function(){return Ke}),n.d(t,"backgroundPosition",function(){return Xe}),n.d(t,"backgroundRepeat",function(){return Ge}),n.d(t,"zIndex",function(){return Ye}),n.d(t,"top",function(){return Qe}),n.d(t,"right",function(){return Je}),n.d(t,"bottom",function(){return Ze}),n.d(t,"left",function(){return et}),n.d(t,"style",function(){return tt}),n.d(t,"get",function(){return s}),n.d(t,"createParser",function(){return c}),n.d(t,"createStyleFunction",function(){return p}),n.d(t,"compose",function(){return m}),n.d(t,"system",function(){return h}),n.d(t,"margin",function(){return W}),n.d(t,"padding",function(){return B}),n.d(t,"space",function(){return U}),n.d(t,"color",function(){return y}),n.d(t,"layout",function(){return v}),n.d(t,"typography",function(){return k}),n.d(t,"flexbox",function(){return O}),n.d(t,"border",function(){return T}),n.d(t,"background",function(){return N}),n.d(t,"position",function(){return M}),n.d(t,"grid",function(){return E}),n.d(t,"shadow",function(){return H}),n.d(t,"boxShadow",function(){return $}),n.d(t,"textShadow",function(){return $}),n.d(t,"variant",function(){return V}),n.d(t,"buttonStyle",function(){return q}),n.d(t,"textStyle",function(){return K}),n.d(t,"colorStyle",function(){return X}),n.d(t,"borders",function(){return P});var G=b.width,Y=b.height,Q=b.minWidth,J=b.minHeight,Z=b.maxWidth,ee=b.maxHeight,te=b.size,ne=b.verticalAlign,re=b.display,oe=b.overflow,ae=x.opacity,ie=w.fontSize,le=w.fontFamily,ue=w.fontWeight,se=w.lineHeight,ce=w.textAlign,de=w.fontStyle,fe=w.letterSpacing,pe=S.alignItems,he=S.alignContent,me=S.justifyItems,ve=S.justifyContent,be=S.flexWrap,ge=S.flexDirection,ye=S.flex,xe=S.flexGrow,ke=S.flexShrink,we=S.flexBasis,Oe=S.justifySelf,Se=S.alignSelf,Ce=S.order,Ee=j.gridGap,je=j.gridColumnGap,Te=j.gridRowGap,Pe=j.gridColumn,Re=j.gridRow,Ne=j.gridAutoFlow,_e=j.gridAutoColumns,Ae=j.gridAutoRows,Me=j.gridTemplateColumns,Ie=j.gridTemplateRows,ze=j.gridTemplateAreas,De=j.gridArea,Le=P.borderWidth,Fe=P.borderStyle,We=P.borderColor,Be=P.borderTop,Ue=P.borderRight,He=P.borderBottom,$e=P.borderLeft,Ve=P.borderRadius,qe=_.backgroundImage,Ke=_.backgroundSize,Xe=_.backgroundPosition,Ge=_.backgroundRepeat,Ye=I.zIndex,Qe=I.top,Je=I.right,Ze=I.bottom,et=I.left,tt=function(e){var t=e.prop,n=e.cssProperty,r=e.alias,o=e.key,a=e.transformValue,i=e.scale,l=e.properties,u={};return u[t]=p({properties:l,property:n||t,scale:o,defaultScale:i,transform:a}),r&&(u[r]=u[t]),c(u)}},function(e,t,n){"use strict";n.d(t,"b",function(){return r}),n.d(t,"a",function(){return o});var r=function(e){return e.scrollTop};function o(e,t){var n=e.timeout,r=e.style,o=void 0===r?{}:r;return{duration:o.transitionDuration||"number"===typeof n?n:n[t.mode]||0,delay:o.transitionDelay}}},,function(e,t,n){"use strict";n.d(t,"a",function(){return a});var r=n(104),o=n(25);function a(){return Object(r.a)()||o.a}},function(e,t,n){"use strict";t.a=function(e){return e&&e.ownerDocument||document}},function(e,t,n){"use strict";var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(o){return!1}}()?Object.assign:function(e,t){for(var n,i,l=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),u=1;u0&&void 0!==arguments[0]?arguments[0]:["all"],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.duration,l=void 0===n?a.standard:n,u=t.easing,s=void 0===u?o.easeInOut:u,c=t.delay,d=void 0===c?0:c;Object(r.a)(t,["duration","easing","delay"]);return(Array.isArray(e)?e:[e]).map(function(e){return"".concat(e," ").concat("string"===typeof l?l:i(l)," ").concat(s," ").concat("string"===typeof d?d:i(d))}).join(",")},getAutoHeightDuration:function(e){if(!e)return 0;var t=e/36;return Math.round(10*(4+15*Math.pow(t,.25)+t/5))}}},function(e,t,n){"use strict";function r(e){var t=e.props,n=e.states,r=e.muiFormControl;return n.reduce(function(e,n){return e[n]=t[n],r&&"undefined"===typeof t[n]&&(e[n]=r[n]),e},{})}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";var r=function(e){return function(e){return!!e&&"object"===typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===o}(e)}(e)};var o="function"===typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function a(e,t){return!1!==t.clone&&t.isMergeableObject(e)?s((n=e,Array.isArray(n)?[]:{}),e,t):e;var n}function i(e,t,n){return e.concat(t).map(function(e){return a(e,n)})}function l(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter(function(t){return e.propertyIsEnumerable(t)}):[]}(e))}function u(e,t,n){var r={};return n.isMergeableObject(e)&&l(e).forEach(function(t){r[t]=a(e[t],n)}),l(t).forEach(function(o){n.isMergeableObject(t[o])&&e[o]?r[o]=function(e,t){if(!t.customMerge)return s;var n=t.customMerge(e);return"function"===typeof n?n:s}(o,n)(e[o],t[o],n):r[o]=a(t[o],n)}),r}function s(e,t,n){(n=n||{}).arrayMerge=n.arrayMerge||i,n.isMergeableObject=n.isMergeableObject||r;var o=Array.isArray(t);return o===Array.isArray(e)?o?n.arrayMerge(e,t,n):u(e,t,n):a(t,n)}s.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce(function(e,n){return s(e,n,t)},{})};var c=s;e.exports=c},function(e,t,n){"use strict";var r=n(0),o=n.n(r).a.createContext({});t.a=o},function(e,t,n){"use strict";n.d(t,"a",function(){return i});var r=n(0),o=n.n(r),a="undefined"!==typeof window?o.a.useLayoutEffect:o.a.useEffect;function i(e){var t=o.a.useRef(e);return a(function(){t.current=e}),o.a.useCallback(function(e){return(0,t.current)(e)},[])}},function(e,t,n){"use strict";function r(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";var r=n(77),o=Object(r.a)();t.a=o},function(e,t,n){"use strict";var r=n(0),o=n.n(r).a.createContext();t.a=o},function(e,t,n){"use strict";n.d(t,"b",function(){return i});var r=n(0),o=n.n(r),a=o.a.createContext();function i(){return o.a.useContext(a)}t.a=a},function(e,t,n){"use strict";function r(e){return null!=e&&!(Array.isArray(e)&&0===e.length)}function o(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return e&&(r(e.value)&&""!==e.value||t&&r(e.defaultValue)&&""!==e.defaultValue)}function a(e){return e.startAdornment}n.d(t,"b",function(){return o}),n.d(t,"a",function(){return a})},function(e,t,n){"use strict";var r=n(0),o=n.n(r);t.a=o.a.createContext(null)},,function(e,t){e.exports=function(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";t.a=function(e){var t={};return function(n){return void 0===t[n]&&(t[n]=e(n)),t[n]}}},function(e,t,n){"use strict";function r(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:166;function r(){for(var r=arguments.length,o=new Array(r),a=0;a=0||(o[n]=e[n]);return o}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";function r(e){if(Symbol.iterator in Object(e)||"[object Arguments]"===Object.prototype.toString.call(e))return Array.from(e)}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";function r(e,t){if(null==e)return{};var n,r,o={},a=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}n.d(t,"a",function(){return r})},function(e,t,n){"use strict";var r=n(98),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},a={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},l={};function u(e){return r.isMemo(e)?i:l[e.$$typeof]||o}l[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0};var s=Object.defineProperty,c=Object.getOwnPropertyNames,d=Object.getOwnPropertySymbols,f=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var o=p(n);o&&o!==h&&e(t,o,r)}var i=c(n);d&&(i=i.concat(d(n)));for(var l=u(t),m=u(n),v=0;v=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}n.d(t,"a",function(){return r})},,,function(e,t,n){"use strict";function r(e){return(r="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e){return(o="function"===typeof Symbol&&"symbol"===r(Symbol.iterator)?function(e){return r(e)}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":r(e)})(e)}n.d(t,"a",function(){return o})},function(e,t,n){"use strict";var r=n(31);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=r(n(0)),a=(0,r(n(37)).default)(o.default.createElement(o.default.Fragment,null,o.default.createElement("path",{d:"M19 3H4.99c-1.11 0-1.98.9-1.98 2L3 19c0 1.1.88 2 1.99 2H19c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 12h-4c0 1.66-1.35 3-3 3s-3-1.34-3-3H4.99V5H19v10zm-3-5h-2V7h-4v3H8l4 4 4-4z"}),o.default.createElement("path",{fill:"none",d:"M0 0h24v24H0V0z"})),"MoveToInbox");t.default=a},function(e,t,n){"use strict";var r=n(31);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=r(n(0)),a=(0,r(n(37)).default)(o.default.createElement("path",{d:"M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"}),"Mail");t.default=a},function(e,t,n){"use strict";var r=n(31);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=r(n(0)),a=(0,r(n(37)).default)(o.default.createElement("path",{d:"M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"}),"ExpandMore");t.default=a},,,,function(e,t,n){"use strict";n.r(t);var r=n(72);n.d(t,"default",function(){return r.a})},function(e,t,n){"use strict";var r=n(1),o=n(2),a=n(0),i=n.n(a),l=(n(5),n(3)),u=n(4),s=n(7),c=i.a.forwardRef(function(e,t){var n=e.children,a=e.classes,u=e.className,c=e.color,d=void 0===c?"inherit":c,f=e.component,p=void 0===f?"svg":f,h=e.fontSize,m=void 0===h?"default":h,v=e.htmlColor,b=e.titleAccess,g=e.viewBox,y=void 0===g?"0 0 24 24":g,x=Object(o.a)(e,["children","classes","className","color","component","fontSize","htmlColor","titleAccess","viewBox"]);return i.a.createElement(p,Object(r.a)({className:Object(l.a)(a.root,u,"inherit"!==d&&a["color".concat(Object(s.a)(d))],"default"!==m&&a["fontSize".concat(Object(s.a)(m))]),focusable:"false",viewBox:y,color:v,"aria-hidden":b?"false":"true",role:b?"img":"presentation",ref:t},x),n,b?i.a.createElement("title",null,b):null)});c.muiName="SvgIcon",t.a=Object(u.a)(function(e){return{root:{userSelect:"none",width:"1em",height:"1em",display:"inline-block",fill:"currentColor",flexShrink:0,fontSize:e.typography.pxToRem(24),transition:e.transitions.create("fill",{duration:e.transitions.duration.shorter})},colorPrimary:{color:e.palette.primary.main},colorSecondary:{color:e.palette.secondary.main},colorAction:{color:e.palette.action.active},colorError:{color:e.palette.error.main},colorDisabled:{color:e.palette.action.disabled},fontSizeInherit:{fontSize:"inherit"},fontSizeSmall:{fontSize:e.typography.pxToRem(20)},fontSizeLarge:{fontSize:e.typography.pxToRem(35)}}},{name:"MuiSvgIcon"})(c)},function(e,t,n){var r=n(93);e.exports=function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};return Object(o.a)({paddingLeft:t(2),paddingRight:t(2)},n,Object(r.a)({},e.up("sm"),Object(o.a)({paddingLeft:t(3),paddingRight:t(3)},n[e.up("sm")])))},toolbar:(a={minHeight:56},Object(r.a)(a,"".concat(e.up("xs")," and (orientation: landscape)"),{minHeight:48}),Object(r.a)(a,e.up("sm"),{minHeight:64}),a)},n)}var f={50:"#e8eaf6",100:"#c5cae9",200:"#9fa8da",300:"#7986cb",400:"#5c6bc0",500:"#3f51b5",600:"#3949ab",700:"#303f9f",800:"#283593",900:"#1a237e",A100:"#8c9eff",A200:"#536dfe",A400:"#3d5afe",A700:"#304ffe"},p={50:"#fce4ec",100:"#f8bbd0",200:"#f48fb1",300:"#f06292",400:"#ec407a",500:"#e91e63",600:"#d81b60",700:"#c2185b",800:"#ad1457",900:"#880e4f",A100:"#ff80ab",A200:"#ff4081",A400:"#f50057",A700:"#c51162"},h={50:"#fafafa",100:"#f5f5f5",200:"#eeeeee",300:"#e0e0e0",400:"#bdbdbd",500:"#9e9e9e",600:"#757575",700:"#616161",800:"#424242",900:"#212121",A100:"#d5d5d5",A200:"#aaaaaa",A400:"#303030",A700:"#616161"},m={50:"#ffebee",100:"#ffcdd2",200:"#ef9a9a",300:"#e57373",400:"#ef5350",500:"#f44336",600:"#e53935",700:"#d32f2f",800:"#c62828",900:"#b71c1c",A100:"#ff8a80",A200:"#ff5252",A400:"#ff1744",A700:"#d50000"},v={black:"#000",white:"#fff"},b=n(11),g={text:{primary:"rgba(0, 0, 0, 0.87)",secondary:"rgba(0, 0, 0, 0.54)",disabled:"rgba(0, 0, 0, 0.38)",hint:"rgba(0, 0, 0, 0.38)"},divider:"rgba(0, 0, 0, 0.12)",background:{paper:v.white,default:h[50]},action:{active:"rgba(0, 0, 0, 0.54)",hover:"rgba(0, 0, 0, 0.08)",hoverOpacity:.08,selected:"rgba(0, 0, 0, 0.14)",disabled:"rgba(0, 0, 0, 0.26)",disabledBackground:"rgba(0, 0, 0, 0.12)"}},y={text:{primary:v.white,secondary:"rgba(255, 255, 255, 0.7)",disabled:"rgba(255, 255, 255, 0.5)",hint:"rgba(255, 255, 255, 0.5)",icon:"rgba(255, 255, 255, 0.5)"},divider:"rgba(255, 255, 255, 0.12)",background:{paper:h[800],default:"#303030"},action:{active:v.white,hover:"rgba(255, 255, 255, 0.1)",hoverOpacity:.1,selected:"rgba(255, 255, 255, 0.2)",disabled:"rgba(255, 255, 255, 0.3)",disabledBackground:"rgba(255, 255, 255, 0.12)"}};function x(e,t,n,r){e[t]||(e.hasOwnProperty(n)?e[t]=e[n]:"light"===t?e.light=Object(b.d)(e.main,r):"dark"===t&&(e.dark=Object(b.a)(e.main,1.5*r)))}function k(e){return Math.round(1e5*e)/1e5}var w={textTransform:"uppercase"},O='"Roboto", "Helvetica", "Arial", sans-serif';function S(e,t){var n="function"===typeof t?t(e):t,r=n.fontFamily,i=void 0===r?O:r,u=n.fontSize,s=void 0===u?14:u,c=n.fontWeightLight,d=void 0===c?300:c,f=n.fontWeightRegular,p=void 0===f?400:f,h=n.fontWeightMedium,m=void 0===h?500:h,v=n.fontWeightBold,b=void 0===v?700:v,g=n.htmlFontSize,y=void 0===g?16:g,x=n.allVariants,S=Object(a.a)(n,["fontFamily","fontSize","fontWeightLight","fontWeightRegular","fontWeightMedium","fontWeightBold","htmlFontSize","allVariants"]),C=s/14,E=function(e){return"".concat(e/y*C,"rem")},j=function(e,t,n,r,a){return Object(o.a)({fontFamily:i,fontWeight:e,fontSize:E(t),lineHeight:n},i===O?{letterSpacing:"".concat(k(r/t),"em")}:{},{},a,{},x)},T={h1:j(d,96,1,-1.5),h2:j(d,60,1,-.5),h3:j(p,48,1.04,0),h4:j(p,34,1.17,.25),h5:j(p,24,1.33,0),h6:j(m,20,1.6,.15),subtitle1:j(p,16,1.75,.15),subtitle2:j(m,14,1.57,.1),body1:j(p,16,1.5,.15),body2:j(p,14,1.43,.15),button:j(m,14,1.75,.4,w),caption:j(p,12,1.66,.4),overline:j(p,12,2.66,1,w)};return l()(Object(o.a)({htmlFontSize:y,pxToRem:E,round:k,fontFamily:i,fontSize:s,fontWeightLight:d,fontWeightRegular:p,fontWeightMedium:m,fontWeightBold:b},T),S,{clone:!1})}var C=.2,E=.14,j=.12;function T(){return["".concat(arguments.length<=0?void 0:arguments[0],"px ").concat(arguments.length<=1?void 0:arguments[1],"px ").concat(arguments.length<=2?void 0:arguments[2],"px ").concat(arguments.length<=3?void 0:arguments[3],"px rgba(0,0,0,").concat(C,")"),"".concat(arguments.length<=4?void 0:arguments[4],"px ").concat(arguments.length<=5?void 0:arguments[5],"px ").concat(arguments.length<=6?void 0:arguments[6],"px ").concat(arguments.length<=7?void 0:arguments[7],"px rgba(0,0,0,").concat(E,")"),"".concat(arguments.length<=8?void 0:arguments[8],"px ").concat(arguments.length<=9?void 0:arguments[9],"px ").concat(arguments.length<=10?void 0:arguments[10],"px ").concat(arguments.length<=11?void 0:arguments[11],"px rgba(0,0,0,").concat(j,")")].join(",")}var P=["none",T(0,1,3,0,0,1,1,0,0,2,1,-1),T(0,1,5,0,0,2,2,0,0,3,1,-2),T(0,1,8,0,0,3,4,0,0,3,3,-2),T(0,2,4,-1,0,4,5,0,0,1,10,0),T(0,3,5,-1,0,5,8,0,0,1,14,0),T(0,3,5,-1,0,6,10,0,0,1,18,0),T(0,4,5,-2,0,7,10,1,0,2,16,1),T(0,5,5,-3,0,8,10,1,0,3,14,2),T(0,5,6,-3,0,9,12,1,0,3,16,2),T(0,6,6,-3,0,10,14,1,0,4,18,3),T(0,6,7,-4,0,11,15,1,0,4,20,3),T(0,7,8,-4,0,12,17,2,0,5,22,4),T(0,7,8,-4,0,13,19,2,0,5,24,4),T(0,7,9,-4,0,14,21,2,0,5,26,4),T(0,8,9,-5,0,15,22,2,0,6,28,5),T(0,8,10,-5,0,16,24,2,0,6,30,5),T(0,8,11,-5,0,17,26,2,0,6,32,5),T(0,9,11,-5,0,18,28,2,0,7,34,6),T(0,9,12,-6,0,19,29,2,0,7,36,6),T(0,10,13,-6,0,20,31,3,0,8,38,7),T(0,10,13,-6,0,21,33,3,0,8,40,7),T(0,10,14,-6,0,22,35,3,0,8,42,7),T(0,11,14,-7,0,23,36,3,0,9,44,8),T(0,11,15,-7,0,24,38,3,0,9,46,8)],R={borderRadius:4};var N=n(18),_=n(54);t.a=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.breakpoints,n=void 0===t?{}:t,r=e.mixins,i=void 0===r?{}:r,u=e.palette,k=void 0===u?{}:u,w=e.shadows,O=e.spacing,C=e.typography,E=void 0===C?{}:C,j=Object(a.a)(e,["breakpoints","mixins","palette","shadows","spacing","typography"]),T=function(e){var t=e.primary,n=void 0===t?{light:f[300],main:f[500],dark:f[700]}:t,r=e.secondary,i=void 0===r?{light:p.A200,main:p.A400,dark:p.A700}:r,u=e.error,s=void 0===u?{light:m[300],main:m[500],dark:m[700]}:u,c=e.type,d=void 0===c?"light":c,k=e.contrastThreshold,w=void 0===k?3:k,O=e.tonalOffset,S=void 0===O?.2:O,C=Object(a.a)(e,["primary","secondary","error","type","contrastThreshold","tonalOffset"]);function E(e){return Object(b.c)(e,y.text.primary)>=w?y.text.primary:g.text.primary}function j(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:500,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:300,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:700;return!(e=Object(o.a)({},e)).main&&e[t]&&(e.main=e[t]),x(e,"light",n,S),x(e,"dark",r,S),e.contrastText||(e.contrastText=E(e.main)),e}var T={dark:y,light:g};return l()(Object(o.a)({common:v,type:d,primary:j(n),secondary:j(i,"A400","A200","A700"),error:j(s),grey:h,contrastThreshold:w,getContrastText:E,augmentColor:j,tonalOffset:S},T[d]),C,{clone:!1})}(k),A=function(e){var t=e.values,n=void 0===t?{xs:0,sm:600,md:960,lg:1280,xl:1920}:t,r=e.unit,i=void 0===r?"px":r,l=e.step,u=void 0===l?5:l,s=Object(a.a)(e,["values","unit","step"]);function d(e){var t="number"===typeof n[e]?n[e]:e;return"@media (min-width:".concat(t).concat(i,")")}function f(e,t){var r=c.indexOf(t)+1;return r===c.length?d(e):"@media (min-width:".concat(n[e]).concat(i,") and ")+"(max-width:".concat(n[c[r]]-u/100).concat(i,")")}return Object(o.a)({keys:c,values:n,up:d,down:function(e){var t=c.indexOf(e)+1,r=n[c[t]];return t===c.length?d("xs"):"@media (max-width:".concat(("number"===typeof r&&t>0?r:e)-u/100).concat(i,")")},between:f,only:function(e){return f(e,e)},width:function(e){return n[e]}},s)}(n),M=function(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:8;if(t.mui)return t;e="function"===typeof t?t:function(e){return t*e};var n=function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,r=t.pulsate,o=void 0!==r&&r,i=t.center,l=void 0===i?a||t.pulsate:i,u=t.fakeElement,s=void 0!==u&&u;if("mousedown"===e.type&&v.current)v.current=!1;else{"touchstart"===e.type&&(v.current=!0);var c,d,f,p=s?null:y.current,h=p?p.getBoundingClientRect():{width:0,height:0,left:0,top:0};if(l||0===e.clientX&&0===e.clientY||!e.clientX&&!e.touches)c=Math.round(h.width/2),d=Math.round(h.height/2);else{var m=e.clientX?e.clientX:e.touches[0].clientX,k=e.clientY?e.clientY:e.touches[0].clientY;c=Math.round(m-h.left),d=Math.round(k-h.top)}if(l)(f=Math.sqrt((2*Math.pow(h.width,2)+Math.pow(h.height,2))/3))%2===0&&(f+=1);else{var w=2*Math.max(Math.abs((p?p.clientWidth:0)-c),c)+2,O=2*Math.max(Math.abs((p?p.clientHeight:0)-d),d)+2;f=Math.sqrt(Math.pow(w,2)+Math.pow(O,2))}e.touches?(g.current=function(){x({pulsate:o,rippleX:c,rippleY:d,rippleSize:f,cb:n})},b.current=setTimeout(function(){g.current&&(g.current(),g.current=null)},80)):x({pulsate:o,rippleX:c,rippleY:d,rippleSize:f,cb:n})}},[a,x]),w=i.a.useCallback(function(){k({},{pulsate:!0})},[k]),O=i.a.useCallback(function(e,t){if(clearTimeout(b.current),"touchend"===e.type&&g.current)return e.persist(),g.current(),g.current=null,void(b.current=setTimeout(function(){O(e,t)}));g.current=null,p(function(e){return e.length>0?e.slice(1):e}),m.current=t},[]);return i.a.useImperativeHandle(t,function(){return{pulsate:w,start:k,stop:O}},[w,k,O]),i.a.createElement("span",Object(r.a)({className:Object(s.a)(l.root,u),ref:y},c),i.a.createElement(z,{component:null,exit:!0},f))});var W,B=Object(f.a)(function(e){return{root:{display:"block",position:"absolute",overflow:"hidden",borderRadius:"inherit",width:"100%",height:"100%",left:0,top:0,pointerEvents:"none",zIndex:0},ripple:{opacity:0,position:"absolute"},rippleVisible:{opacity:.3,transform:"scale(1)",animation:"$mui-ripple-enter ".concat(550,"ms ").concat(e.transitions.easing.easeInOut)},ripplePulsate:{animationDuration:"".concat(e.transitions.duration.shorter,"ms")},child:{opacity:1,display:"block",width:"100%",height:"100%",borderRadius:"50%",backgroundColor:"currentColor"},childLeaving:{opacity:0,animation:"$mui-ripple-exit ".concat(550,"ms ").concat(e.transitions.easing.easeInOut)},childPulsate:{position:"absolute",left:0,top:0,animation:"$mui-ripple-pulsate 2500ms ".concat(e.transitions.easing.easeInOut," 200ms infinite")},"@keyframes mui-ripple-enter":{"0%":{transform:"scale(0)",opacity:.1},"100%":{transform:"scale(1)",opacity:.3}},"@keyframes mui-ripple-exit":{"0%":{opacity:1},"100%":{opacity:0}},"@keyframes mui-ripple-pulsate":{"0%":{transform:"scale(1)"},"50%":{transform:"scale(0.92)"},"100%":{transform:"scale(1)"}}}},{flip:!1,name:"MuiTouchRipple"})(((W=i.a.memo(F)).muiName="MuiTouchRipple",W)),U=i.a.forwardRef(function(e,t){var n=e.action,a=e.buttonRef,l=e.centerRipple,f=void 0!==l&&l,p=e.children,m=e.classes,v=e.className,b=e.component,g=void 0===b?"button":b,y=e.disabled,x=e.disableRipple,k=void 0!==x&&x,w=e.disableTouchRipple,O=void 0!==w&&w,C=e.focusRipple,E=void 0!==C&&C,j=e.focusVisibleClassName,T=e.onBlur,P=e.onClick,R=e.onFocus,N=e.onFocusVisible,_=e.onKeyDown,A=e.onKeyUp,M=e.onMouseDown,I=e.onMouseLeave,z=e.onMouseUp,D=e.onTouchEnd,L=e.onTouchMove,F=e.onTouchStart,W=e.onDragLeave,U=e.tabIndex,H=void 0===U?0:U,$=e.TouchRippleProps,V=e.type,q=void 0===V?"button":V,K=Object(o.a)(e,["action","buttonRef","centerRipple","children","classes","className","component","disabled","disableRipple","disableTouchRipple","focusRipple","focusVisibleClassName","onBlur","onClick","onFocus","onFocusVisible","onKeyDown","onKeyUp","onMouseDown","onMouseLeave","onMouseUp","onTouchEnd","onTouchMove","onTouchStart","onDragLeave","tabIndex","TouchRippleProps","type"]),X=i.a.useRef(null);var G=i.a.useRef(null),Y=i.a.useState(!1),Q=Y[0],J=Y[1];y&&Q&&J(!1);var Z=S(),ee=Z.isFocusVisible,te=Z.onBlurVisible,ne=Z.ref;function re(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:O;return Object(d.a)(function(r){return t&&t(r),!(r.defaultPrevented||n)&&G.current&&G.current[e](r),!0})}i.a.useImperativeHandle(n,function(){return{focusVisible:function(){J(!0),X.current.focus()}}},[]),i.a.useEffect(function(){Q&&E&&!k&&G.current.pulsate()},[k,E,Q]);var oe=re("start",M),ae=re("stop",W),ie=re("stop",z),le=re("stop",function(e){Q&&e.preventDefault(),I&&I(e)}),ue=re("start",F),se=re("stop",D),ce=re("stop",L),de=re("stop",function(e){Q&&(te(e),J(!1)),T&&T(e)},!1),fe=Object(d.a)(function(e){y||(X.current||(X.current=e.currentTarget),ee(e)&&(J(!0),N&&N(e)),R&&R(e))}),pe=i.a.useRef(!1),he=Object(d.a)(function(e){E&&!pe.current&&Q&&G.current&&" "===e.key&&(pe.current=!0,e.persist(),G.current.stop(e,function(){G.current.start(e)})),_&&_(e);var t=u.a.findDOMNode(X.current);e.target!==e.currentTarget||!g||"button"===g||" "!==e.key&&"Enter"!==e.key||"A"===t.tagName&&t.href||(e.preventDefault(),P&&P(e))}),me=Object(d.a)(function(e){E&&" "===e.key&&G.current&&Q&&(pe.current=!1,e.persist(),G.current.stop(e,function(){G.current.pulsate(e)})),A&&A(e)}),ve=Object(s.a)(m.root,v,Q&&[m.focusVisible,j],y&&m.disabled),be=g;"button"===be&&K.href&&(be="a");var ge={};"button"===be?(ge.type=q,ge.disabled=y):("a"===be&&K.href||(ge.role="button"),ge["aria-disabled"]=y);var ye=Object(c.c)(a,t),xe=Object(c.c)(ne,X),ke=Object(c.c)(ye,xe);return i.a.createElement(be,Object(r.a)({className:ve,onBlur:de,onClick:P,onFocus:fe,onKeyDown:he,onKeyUp:me,onMouseDown:oe,onMouseLeave:le,onMouseUp:ie,onDragLeave:ae,onTouchEnd:se,onTouchMove:ce,onTouchStart:ue,ref:ke,tabIndex:y?-1:H},ge,K),p,k||y?null:i.a.createElement(h,null,i.a.createElement(B,Object(r.a)({ref:G,center:f},$))))});t.a=Object(f.a)({root:{display:"inline-flex",alignItems:"center",justifyContent:"center",position:"relative",WebkitTapHighlightColor:"transparent",backgroundColor:"transparent",outline:0,border:0,margin:0,borderRadius:0,padding:0,cursor:"pointer",userSelect:"none",verticalAlign:"middle","-moz-appearance":"none","-webkit-appearance":"none",textDecoration:"none",color:"inherit","&::-moz-focus-inner":{borderStyle:"none"},"&$disabled":{pointerEvents:"none",cursor:"default"}},disabled:{},focusVisible:{}},{name:"MuiButtonBase"})(U)},,,,,,,,,function(e,t,n){"use strict";var r=n(17),o="function"===typeof Symbol&&Symbol.for,a=o?Symbol.for("react.element"):60103,i=o?Symbol.for("react.portal"):60106,l=o?Symbol.for("react.fragment"):60107,u=o?Symbol.for("react.strict_mode"):60108,s=o?Symbol.for("react.profiler"):60114,c=o?Symbol.for("react.provider"):60109,d=o?Symbol.for("react.context"):60110,f=o?Symbol.for("react.concurrent_mode"):60111,p=o?Symbol.for("react.forward_ref"):60112,h=o?Symbol.for("react.suspense"):60113,m=o?Symbol.for("react.memo"):60115,v=o?Symbol.for("react.lazy"):60116,b="function"===typeof Symbol&&Symbol.iterator;function g(e){for(var t=arguments.length-1,n="https://reactjs.org/docs/error-decoder.html?invariant="+e,r=0;r_.length&&_.push(e)}function I(e,t,n){return null==e?0:function e(t,n,r,o){var l=typeof t;"undefined"!==l&&"boolean"!==l||(t=null);var u=!1;if(null===t)u=!0;else switch(l){case"string":case"number":u=!0;break;case"object":switch(t.$$typeof){case a:case i:u=!0}}if(u)return r(o,t,""===n?"."+z(t,0):n),1;if(u=0,n=""===n?".":n+":",Array.isArray(t))for(var s=0;sthis.eventPool.length&&this.eventPool.push(e)}function de(e){e.eventPool=[],e.getPooled=se,e.release=ce}o(ue.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!==typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=ie)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!==typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=ie)},persist:function(){this.isPersistent=ie},isPersistent:le,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;this.nativeEvent=this._targetInst=this.dispatchConfig=null,this.isPropagationStopped=this.isDefaultPrevented=le,this._dispatchInstances=this._dispatchListeners=null}}),ue.Interface={type:null,target:null,currentTarget:function(){return null},eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null},ue.extend=function(e){function t(){}function n(){return r.apply(this,arguments)}var r=this;t.prototype=r.prototype;var a=new t;return o(a,n.prototype),n.prototype=a,n.prototype.constructor=n,n.Interface=o({},r.Interface,e),n.extend=r.extend,de(n),n},de(ue);var fe=ue.extend({data:null}),pe=ue.extend({data:null}),he=[9,13,27,32],me=V&&"CompositionEvent"in window,ve=null;V&&"documentMode"in document&&(ve=document.documentMode);var be=V&&"TextEvent"in window&&!ve,ge=V&&(!me||ve&&8=ve),ye=String.fromCharCode(32),xe={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},ke=!1;function we(e,t){switch(e){case"keyup":return-1!==he.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Oe(e){return"object"===typeof(e=e.detail)&&"data"in e?e.data:null}var Se=!1;var Ce={eventTypes:xe,extractEvents:function(e,t,n,r){var o=void 0,a=void 0;if(me)e:{switch(e){case"compositionstart":o=xe.compositionStart;break e;case"compositionend":o=xe.compositionEnd;break e;case"compositionupdate":o=xe.compositionUpdate;break e}o=void 0}else Se?we(e,n)&&(o=xe.compositionEnd):"keydown"===e&&229===n.keyCode&&(o=xe.compositionStart);return o?(ge&&"ko"!==n.locale&&(Se||o!==xe.compositionStart?o===xe.compositionEnd&&Se&&(a=ae()):(re="value"in(ne=r)?ne.value:ne.textContent,Se=!0)),o=fe.getPooled(o,t,n,r),a?o.data=a:null!==(a=Oe(n))&&(o.data=a),$(o),a=o):a=null,(e=be?function(e,t){switch(e){case"compositionend":return Oe(t);case"keypress":return 32!==t.which?null:(ke=!0,ye);case"textInput":return(e=t.data)===ye&&ke?null:e;default:return null}}(e,n):function(e,t){if(Se)return"compositionend"===e||!me&&we(e,t)?(e=ae(),oe=re=ne=null,Se=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1