diff --git a/.github/workflows/alt_linux_distros.yml b/.github/workflows/alt_linux_distros.yml index 5583b2354..69223250e 100644 --- a/.github/workflows/alt_linux_distros.yml +++ b/.github/workflows/alt_linux_distros.yml @@ -141,7 +141,7 @@ jobs: - name: Install GTest run: ${{matrix.install_gtest}} - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure Trick run: | export MAKEFLAGS=-j`nproc` diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index 32abf3e38..9db3e406b 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -32,7 +32,7 @@ jobs: dnf install -y gtest-devel gmock-devel - name: Checkout repository - uses: actions/checkout@master + uses: actions/checkout@v4 - name: Configure Trick run: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index bfb208092..9b5d4e93b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -10,7 +10,7 @@ jobs: container: docker://nasatrick/${{matrix.container}}:latest strategy: matrix: - container: [trick_centos7, trick_centos8, trick_ubuntu1804] + container: [trick_ubuntu1804] steps: - name: run tests run: | diff --git a/.github/workflows/python_tests_32.yml b/.github/workflows/python_tests_32.yml deleted file mode 100644 index 74754de03..000000000 --- a/.github/workflows/python_tests_32.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Python Tests 32-bit - -on: - push: - # Temporarily disable all tests for Issue #1188. - branches-ignore: - - '**' - paths-ignore: - - 'docs/**' - - '.github/workflows/**' - - '!.github/workflows/python_tests_32.yml' - pull_request: - # Temporarily disable all tests for Issue #1188. - branches-ignore: - - '**' - -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 index 712a2b4cd..803f90790 100644 --- a/.github/workflows/python_tests_linux.yml +++ b/.github/workflows/python_tests_linux.yml @@ -24,8 +24,6 @@ jobs: - { 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 @@ -102,13 +100,6 @@ jobs: - 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 @@ -123,21 +114,6 @@ jobs: 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: > - sed -i -e "s|mirrorlist=|#mirrorlist=|g" -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-Linux-* && - 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}} @@ -164,7 +140,7 @@ jobs: - name: Install GTest run: ${{matrix.install_gtest}} - name: Checkout repository - uses: actions/checkout@master + uses: actions/checkout@v4 - name: Info after checkout run: | pwd diff --git a/.github/workflows/python_tests_macos.yml b/.github/workflows/python_tests_macos.yml index b0027d273..10e7b8dc5 100644 --- a/.github/workflows/python_tests_macos.yml +++ b/.github/workflows/python_tests_macos.yml @@ -19,7 +19,7 @@ jobs: runs-on: macos-latest steps: - name: Checkout repository - uses: actions/checkout@master + uses: actions/checkout@v4 - name: Install python run: | brew install python diff --git a/.github/workflows/report_linux.yml b/.github/workflows/report_linux.yml index 668be883f..907dc2b42 100644 --- a/.github/workflows/report_linux.yml +++ b/.github/workflows/report_linux.yml @@ -13,7 +13,6 @@ jobs: cfg: #-------- Operating Systems ---------------- - { os: ubuntu, tag: 22.04, arch: debian, arch_ver: 12 } - - { os: centos, tag: 7, arch: rhel, arch_ver: 7 } - { os: rockylinux, tag: 8, arch: rhel, arch_ver: 8 } - { os: oraclelinux, tag: 8, arch: rhel, arch_ver: 8 } python: [3] diff --git a/.github/workflows/report_linux_py2.yml b/.github/workflows/report_linux_py2.yml index d324d7b89..952e4a2a3 100644 --- a/.github/workflows/report_linux_py2.yml +++ b/.github/workflows/report_linux_py2.yml @@ -13,7 +13,6 @@ jobs: cfg: #-------- Operating Systems ---------------- - { os: ubuntu, tag: 22.04, arch: debian, arch_ver: 12 } - - { os: centos, tag: 7, arch: rhel, arch_ver: 7 } - { os: rockylinux, tag: 8, arch: rhel, arch_ver: 8 } - { os: oraclelinux, tag: 8, arch: rhel, arch_ver: 8 } python: [2] diff --git a/.github/workflows/test_32.yml b/.github/workflows/test_32.yml deleted file mode 100644 index d96b7cfb8..000000000 --- a/.github/workflows/test_32.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: 32-bit - -on: - schedule: - - cron: '0 0 * * 0' - workflow_dispatch: - -jobs: - trick_32bit: - runs-on: ubuntu-latest - container: docker://centos:7 - steps: - - name: Add yum repo - run: yum -y install epel-release.noarch && yum -y update - - 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 libgcc*i686 libstdc++*i686 glibc*i686 - - 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/current/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: Checkout repository - uses: actions/checkout@v3 - - name: Build Trick - run: | - yum -y update && yum clean all - export MAKEFLAGS=-j`nproc` - ./configure --without-hdf5 --enable-32bit - make - - name: Run tests - run: | - cd share/trick/trickops/ - yum install -y python3-devel - python3 -m venv .venv && . .venv/bin/activate && pip install --upgrade pip && pip3 install -r requirements.txt - cd ../../../ - echo $MAKEFLAGS $CXXFLAGS $CFLAGS - make test32 diff --git a/.github/workflows/test_32_oracle.yml b/.github/workflows/test_32_oracle.yml index c0850e095..b9d23d16d 100644 --- a/.github/workflows/test_32_oracle.yml +++ b/.github/workflows/test_32_oracle.yml @@ -56,7 +56,7 @@ jobs: make make install - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build Trick run: | export CFLAGS="-m32" diff --git a/.github/workflows/test_linux.yml b/.github/workflows/test_linux.yml index 135501b76..1c58ce6e3 100644 --- a/.github/workflows/test_linux.yml +++ b/.github/workflows/test_linux.yml @@ -23,7 +23,6 @@ jobs: cfg: #-------- Operating Systems ---------------- - { os: ubuntu, tag: 22.04, arch: debian, arch_ver: 12 } - - { os: centos, tag: 7, arch: rhel, arch_ver: 7 } - { os: rockylinux, tag: 8, arch: rhel, arch_ver: 8 } - { os: oraclelinux, tag: 8, arch: rhel, arch_ver: 8 } python: [3] @@ -95,30 +94,6 @@ jobs: zlib-devel python2-devel python3-devel -#-------- RHEL 7-based Only Dependencies ---------------- - - cfg: { arch: rhel, arch_ver: 7 } - pkg_mgr: yum - conf_pkg: | - sed -i -e "s|mirrorlist=|#mirrorlist=|g" -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* && - yum -y install epel-release - yum -y update - yum -y install wget - os_deps: >- - libX11-devel - libXt-devel - swig3 - install_gtest: | - cd /tmp - wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz - tar xzvf release-1.8.0.tar.gz - cd /tmp/googletest-release-1.8.0/googletest - cmake . - make - make install - cd /tmp/googletest-release-1.8.0/googlemock - cmake . - make - make install #-------- RHEL 8-based Only Dependencies ---------------- - cfg: { arch: rhel, arch_ver: 8 } pkg_mgr: dnf @@ -154,7 +129,7 @@ jobs: - name: Install GTest run: ${{matrix.install_gtest}} - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Configure Trick run: | export MAKEFLAGS=-j`nproc` @@ -171,7 +146,7 @@ jobs: python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt cd ../../../; make test - name: Upload Tests - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v4 if: success() || failure() # run this step even if previous step failed with: name: Trick_${{matrix.cfg.os}}${{matrix.cfg.tag}}_py${{matrix.python}} diff --git a/.github/workflows/test_linux_py2.yml b/.github/workflows/test_linux_py2.yml index f14fc1c40..6796a0dbe 100644 --- a/.github/workflows/test_linux_py2.yml +++ b/.github/workflows/test_linux_py2.yml @@ -13,7 +13,6 @@ jobs: cfg: #-------- Operating Systems ---------------- - { os: ubuntu, tag: 22.04, arch: debian, arch_ver: 12 } - - { os: centos, tag: 7, arch: rhel, arch_ver: 7 } - { os: rockylinux, tag: 8, arch: rhel, arch_ver: 8 } - { os: oraclelinux, tag: 8, arch: rhel, arch_ver: 8 } python: [2] @@ -130,7 +129,7 @@ jobs: - name: Install GTest run: ${{matrix.install_gtest}} - name: Checkout repository - uses: actions/checkout@master + uses: actions/checkout@v4 - name: Configure Trick run: | export MAKEFLAGS=-j`nproc` @@ -146,7 +145,7 @@ jobs: python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt cd ../../../; make test - name: Upload Tests - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v4 if: success() || failure() # run this step even if previous step failed with: name: Trick_${{matrix.cfg.os}}${{matrix.cfg.tag}}_py${{matrix.python}} diff --git a/.github/workflows/test_macos.yml b/.github/workflows/test_macos.yml index d232eadf0..5d4fe860c 100644 --- a/.github/workflows/test_macos.yml +++ b/.github/workflows/test_macos.yml @@ -16,7 +16,7 @@ jobs: runs-on: macos-13 steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install gtest run: | brew install googletest @@ -42,7 +42,7 @@ jobs: export MAKEFLAGS=-j4 cd ../../../; make test - name: Upload Tests - uses: actions/upload-artifact@v3.0.0 + uses: actions/upload-artifact@v4 if: success() || failure() # run this step even if previous step failed with: name: Trick_macos diff --git a/.github/workflows/trickops.yml b/.github/workflows/trickops.yml index d7a8dc8a3..71c653639 100644 --- a/.github/workflows/trickops.yml +++ b/.github/workflows/trickops.yml @@ -20,7 +20,7 @@ jobs: - name: create virtual environment run: | cd share/trick/trickops/ - python3 -m venv .venv && source .venv/bin/activate && pip3 install -r requirements.txt + python3 -m venv .venv && source .venv/bin/activate && pip3 install --upgrade pip && pip3 install -r requirements.txt - name: get and build koviz run: | cd /tmp/ && wget -q https://github.com/nasa/koviz/archive/refs/heads/master.zip && unzip master.zip @@ -31,10 +31,10 @@ jobs: source ../.venv/bin/activate export PATH="/tmp/koviz-master/bin:${PATH}" ./run_tests.py - - uses: actions/upload-artifact@v3.0.0 + - uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: doctests + name: doctests_${{ github.run_id }}_${{ github.job }} path: | share/trick/trickops/tests/*_doctest_log.txt /tmp/log.* @@ -63,10 +63,10 @@ jobs: source ../.venv/bin/activate export PATH="/tmp/koviz-master/bin:${PATH}" ./run_tests.py - - uses: actions/upload-artifact@v3.0.0 + - uses: actions/upload-artifact@v4 if: ${{ always() }} with: - name: doctests + name: doctests_${{ github.run_id }}_${{ github.job }} path: | share/trick/trickops/tests/*_doctest_log.txt /tmp/log.* diff --git a/README.md b/README.md index 9731c538c..336b1a4ed 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@

-

+

-Linux +Linux -macOS +macOS -32-bit +32-bit

@@ -24,14 +24,14 @@ The Trick Simulation Environment, developed at the NASA Johnson Space Center, is

- - - + + + - - - - + + + + @@ -44,14 +44,14 @@ The Trick Simulation Environment, developed at the NASA Johnson Space Center, is
Install GuideTutorialDocumentation
Install GuideTutorialDocumentation
- - - + + + - - - + + + diff --git a/docs/documentation/building_a_simulation/Model-Source-Code.md b/docs/documentation/building_a_simulation/Model-Source-Code.md index 896845a06..daa143733 100644 --- a/docs/documentation/building_a_simulation/Model-Source-Code.md +++ b/docs/documentation/building_a_simulation/Model-Source-Code.md @@ -408,24 +408,51 @@ Trick may use model code with any type of inheritance. Some limitations are pres ### Namespaces -Currently one level of namespace is supported. Additional levels of namespaces are ignored. Similarly classes and enumerations embedded in other classes are ignored. +ICG supports namespaces and nested scopes. Data recording and variable access via Trick View should work regardless of how many levels there are. -```C++ -namespace my_ns { - // BB is processed - class BB { - public: - std::string str; - // Class CC is ignored. - class CC { - ... +Namespaces and nested scopes are similarly supported in Python contexts, such as the input file and variable server, with some caveats regarding templates. +1. A template instantiation may be unqualified (have no use of the scope resolution operator `::`) only if its corresponding template is declared in the immediately-enclosing namespace. +2. Otherwise, a template instantiation must be fully qualified, starting from the global namespace. +3. Finally, instantiations of templates declared within the same class must be excluded from SWIG. + +In the following examples, all template instantiations occur in `example::prime::Soup`. The immediately-enclosing namespace is `prime`, so only instantiations of templates declared directly in `prime` (only `Celery`) may be unqualified. All other template instantiations must be fully qualified, starting from the global namespace, even if the C++ name lookup process would find them with partial qualification. + +```c++ +template class Potato {}; + +namespace example { + + template class Onion {}; + + namespace peer { + template class Raddish {}; + } + + namespace prime { + + namespace inner { + template class Carrot {}; } - }; - // Everything enclosed in inner_ns is ignored. - namespace inner_ns { - ... - }; -}; + + template class Celery {}; + + class Soup { + + public: + template class Broth {}; + + ::Potato potato; // Rule 2 + example::Onion onion; // Rule 2 + example::peer::Raddish raddish; // Rule 2 + example::prime::inner::Carrot carrot; // Rule 2 + Celery celery; // Rule 1 +#ifndef SWIG + Broth broth; // Rule 3 +#endif + }; + } + +} ``` ### Function Overloading diff --git a/docs/documentation/miscellaneous_trick_tools/TrickOps.md b/docs/documentation/miscellaneous_trick_tools/TrickOps.md index 024c3c498..abde10916 100644 --- a/docs/documentation/miscellaneous_trick_tools/TrickOps.md +++ b/docs/documentation/miscellaneous_trick_tools/TrickOps.md @@ -423,6 +423,57 @@ if ret == 0: # Successful generation Note that the number of runs to-be-generated is configured somewhere in the `input.py` code and this module cannot robustly know that information for any particular use-case. This is why `monte_dir` is a required input to several functions - this directory is processed by the module to understand how many runs were generated. +## `send_hs` - TrickOps Helper Class for Parsing Simulation Diagnostics + +Each Trick simulation run directory contains a number of Trick-generated metadata files, one of which is the `send_hs` file which represents all output during the simulation run sent through the Trick "(h)ealth and (s)tatus" messaging system. At the end of the `send_hs` message, internal diagnostic information is printed by Trick that looks a lot like this: + +``` +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| + REALTIME SHUTDOWN STATS: + ACTUAL INIT TIME: 42.606 + ACTUAL ELAPSED TIME: 55.551 +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| +SIMULATION TERMINATED IN + PROCESS: 0 + ROUTINE: Executive_loop_single_thread.cpp:98 + DIAGNOSTIC: Reached termination time + + SIMULATION START TIME: 0.000 + SIMULATION STOP TIME: 68.955 + SIMULATION ELAPSED TIME: 68.955 + USER CPU TIME USED: 55.690 + SYSTEM CPU TIME USED: 0.935 + SIMULATION / CPU TIME: 1.218 + INITIALIZATION USER CPU TIME: 42.783 + INITIALIZATION SYSTEM CPU TIME: 0.901 + SIMULATION RAM USAGE: 1198.867MB + (External program RAM usage not included!) + VOLUNTARY CONTEXT SWITCHES (INIT): 792 +INVOLUNTARY CONTEXT SWITCHES (INIT): 187 + VOLUNTARY CONTEXT SWITCHES (RUN): 97 + INVOLUNTARY CONTEXT SWITCHES (RUN): 14 +``` + +The information provided here is a summary of how long the simulation ran, in both wall-clock time, and cpu time, both for initialization and run-time, and other useful metrics like how much peak RAM was used during run-time execution. Tracking this information can be useful for Trick-using groups so TrickOps provides a utility class for parsing this data. Here's an example of how you might use the `send_hs` module: + +```python +import send_hs # Import the module +# Instantiate a send_hs instance, reading the given send_hs file +shs = send_hs.send_hs("path/to/SIM_A/RUN_01/send_hs") +start_time = shs.get('SIMULATION START TIME') # Get the value of sim start time +stop_time = shs.get('SIMULATION STOP TIME') # Get the value of sim stop time +realtime_ratio = shs.get('SIMULATION / CPU TIME') # Get the realtime ratio (how fast the sim ran) + +# Instead of getting diagnostics individually, you can ask for the full dictionary +diagnostics = shs.get_diagnostics() +# Print the RAM usage from the dictionary +print(diagnostics['SIMULATION RAM USAGE']) +``` + +Plotting this data for regression scenarios can be quite useful - a lot of groups would want to know if the sim slowed down significantly and if so, when in the history this occurred. If you are already invested in Jenkins CI, you might be interested in using the `send_hs` module in conjunction with the [Jenkins plot plugin](https://plugins.jenkins.io/plot/). Here's an example of what tracking sim realtime ratio over time looks like in a workflow with about 15 regression scenarios all shown on a single plot: + +![ExampleWorkflow In Action](images/sim_speed_example.png) + ## More Information A lot of time was spent adding `python` docstrings to the modules in the `trickops/` directory and tests under the `trickops/tests/`. This README does not cover all functionality, so please see the in-code documentation and unit tests for more detailed information on the framework capabilities. diff --git a/docs/documentation/miscellaneous_trick_tools/images/sim_speed_example.png b/docs/documentation/miscellaneous_trick_tools/images/sim_speed_example.png new file mode 100644 index 000000000..676ed187e Binary files /dev/null and b/docs/documentation/miscellaneous_trick_tools/images/sim_speed_example.png differ diff --git a/docs/documentation/simulation_capabilities/Frame-Logging.md b/docs/documentation/simulation_capabilities/Frame-Logging.md index 4aee523d2..9cf105ad8 100644 --- a/docs/documentation/simulation_capabilities/Frame-Logging.md +++ b/docs/documentation/simulation_capabilities/Frame-Logging.md @@ -1,7 +1,7 @@ | [Home](/trick) → [Documentation Home](../Documentation-Home) → [Simulation Capabilities](Simulation-Capabilities) → Frame Logging | |------------------------------------------------------------------| -Trick provides a means to gather simulation performance data and view it using Data Products (see [Data Products](Data-Products)). +Trick provides a means to gather simulation performance data and view it using Data Products (see [Data Products](../data_products/Data-Products)). When the user turns on the Frame Logging feature, Trick will use its Data Recording mechanism to track the following: - execution time of each Trick and User job, view in Data Products using DP_rt_trickjobs.xml and DP_rt_userjobs.xml - a timeline showing when each job runs during each job frame, view in Data Products using DP_rt_timeline_init.xml and DP_rt_timeline.xml diff --git a/include/trick/DRAscii.hh b/include/trick/DRAscii.hh index bdcc227d3..6da2c8390 100644 --- a/include/trick/DRAscii.hh +++ b/include/trick/DRAscii.hh @@ -13,6 +13,7 @@ PROGRAMMERS: #include "trick/DataRecordGroup.hh" #ifdef SWIG +%feature("compactdefaultargs","0") ; %feature("shadow") Trick::DRAscii::DRAscii(std::string in_name) %{ def __init__(self, *args): this = $action(*args) @@ -68,7 +69,7 @@ namespace Trick { @code = trick.DRAscii("") @endcode @copydoc Trick::DataRecordGroup::DataRecordGroup(string in_name) */ - DRAscii( std::string in_name) ; + DRAscii( std::string in_name, Trick::DR_Type dr_type = Trick::DR_Type::DR_Type_Ascii ) ; /** @copybrief Trick::DataRecordGroup::format_specific_header diff --git a/include/trick/DRBinary.hh b/include/trick/DRBinary.hh index bcf5c4ef4..323ce788e 100644 --- a/include/trick/DRBinary.hh +++ b/include/trick/DRBinary.hh @@ -93,7 +93,7 @@ namespace Trick { @code = trick.DRBinary("") @endcode @copydoc Trick::DataRecordGroup::DataRecordGroup(string in_name) */ - DRBinary( std::string in_name, bool register_group = true ) ; + DRBinary( std::string in_name, bool register_group = true, Trick::DR_Type dr_type = Trick::DR_Type::DR_Type_Binary ) ; /** @copybrief Trick::DataRecordGroup::format_specific_header diff --git a/include/trick/DRHDF5.hh b/include/trick/DRHDF5.hh index a241f20a1..54d5cfbdd 100644 --- a/include/trick/DRHDF5.hh +++ b/include/trick/DRHDF5.hh @@ -24,6 +24,7 @@ PROGRAMMERS: #endif #ifdef SWIG +%feature("compactdefaultargs","0") ; %feature("shadow") Trick::DRHDF5::DRHDF5(std::string in_name) %{ def __init__(self, *args): this = $action(*args) @@ -101,7 +102,7 @@ GROUP "/" { @code = trick.DRHDF5("") @endcode @copydoc Trick::DataRecordGroup::DataRecordGroup(string in_name) */ - DRHDF5( std::string in_name) ; + DRHDF5( std::string in_name, Trick::DR_Type dr_type = Trick::DR_Type::DR_Type_HDF5) ; /** @copybrief Trick::DataRecordGroup::format_specific_header diff --git a/include/trick/DataRecordDispatcher.hh b/include/trick/DataRecordDispatcher.hh index f1509da2b..78d05859d 100644 --- a/include/trick/DataRecordDispatcher.hh +++ b/include/trick/DataRecordDispatcher.hh @@ -75,9 +75,15 @@ namespace Trick { /** @brief Removes all data recording groups. */ void remove_all_groups() ; - /** @brief Gets a data recording group. */ + /** @brief Gets a data recording group by its name. */ Trick::DataRecordGroup * get_group(std::string group_name) ; + /** @brief Gets a data recording group by its id number */ + Trick::DataRecordGroup * get_group(int idx) ; + + /** @brief Gets the size of all added data recroding groups */ + int get_groups_size() ; + /** @brief Signal the write thread to execute. */ virtual int signal_thread() ; diff --git a/include/trick/DataRecordGroup.hh b/include/trick/DataRecordGroup.hh index d65870833..184a65ecb 100644 --- a/include/trick/DataRecordGroup.hh +++ b/include/trick/DataRecordGroup.hh @@ -42,6 +42,14 @@ namespace Trick { DR_Not_Specified = 3 /**< Unknown type */ } ; + enum DR_Type { + DR_Type_None, + DR_Type_Ascii, + DR_Type_Binary, + DR_Type_HDF5, + DR_Type_FrameLogDataRecord + } ; + class DataRecordBuffer { public: char *buffer; /* ** generic holding buffer for data */ @@ -149,7 +157,7 @@ namespace Trick { @brief Constructor that creates a new data recording group with the given @c in_name. @param in_name - the new data recording group name */ - DataRecordGroup( std::string in_name = "" ) ; + DataRecordGroup( std::string in_name = "", Trick::DR_Type dr_type = Trick::DR_Type::DR_Type_None ) ; ~DataRecordGroup() ; @@ -405,6 +413,12 @@ namespace Trick { */ virtual int add_time_variable() ; + /** + @brief This function adds jobs to the DRG based on the dr type. + @returns void + */ + void configure_jobs(DR_Type type) ; + /** Check that a variable is supported by data recording. */ /** Variable must be a single primitive type - no STL, array, structured, string */ bool isSupportedType(REF2 * ref2, std::string& message); diff --git a/include/trick/ExternalApplication.hh b/include/trick/ExternalApplication.hh index 3e93930ed..3b87d9251 100644 --- a/include/trick/ExternalApplication.hh +++ b/include/trick/ExternalApplication.hh @@ -295,13 +295,6 @@ namespace Trick { /** Command to execute when starting this application. */ std::string command; - /** Pointer to alloc'd command c str for use with external application c_intf */ - char * command_c_str; - - /** alloc'd addresses to be deallocated during app destruction (currently only - used by command_c_str) */ - std::vector allocations; - private: /** Prevent SWIG from trying to invoke operator= on ostringstream. */ diff --git a/include/trick/data_record_proto.h b/include/trick/data_record_proto.h index d5b18e354..2e5a427ee 100644 --- a/include/trick/data_record_proto.h +++ b/include/trick/data_record_proto.h @@ -23,6 +23,8 @@ int set_max_size_record_group (const char * in_name, uint64_t bytes ) ; int add_data_record_group( Trick::DataRecordGroup * in_group, Trick::DR_Buffering buffering = Trick::DR_Not_Specified ) ; int remove_data_record_group( Trick::DataRecordGroup * in_group ) ; Trick::DataRecordGroup * get_data_record_group( std::string in_name ) ; +Trick::DataRecordGroup * get_data_record_group_by_idx( int idx ) ; +int get_num_data_record_groups() ; } #endif diff --git a/libexec/trick/convert_swig b/libexec/trick/convert_swig index e3a08c38e..a930a6067 100755 --- a/libexec/trick/convert_swig +++ b/libexec/trick/convert_swig @@ -57,7 +57,6 @@ my %sim ; my %out_of_date ; my ($version, $thread, $year) ; my %processed_templates ; -my $global_template_typedefs ; my $typedef_def = qr/typedef\s+ # the word typedef (?:[_A-Za-z][\s\w]*\s*) # resolved type @@ -336,24 +335,6 @@ sub process_file() { } print OUT "\n$new_contents" ; print OUT "$contents\n" ; - print OUT $global_template_typedefs ; - - # Add _swig_setattr_nondynamic_instance_variable function for raising AttributeError for improper non-class attribute assingment in input processor. - # _swig_setattr_nondynamic_instance_variable function is added for each class in process_class subroutine. - foreach my $c ( @class_names ) { - if ( ! exists $class_typemap_printed{$c} ) { - my $c_ = $c ; - $c_ =~ s/\:/_/g ; - if ( $c !~ /::/ ) { - print OUT "\n#if SWIG_VERSION > 0x040000\n"; - print OUT "%pythoncode %{\n" ; - print OUT " if '$c' in globals():\n"; - print OUT " $c.__setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n" ; - print OUT "%}\n" ; - print OUT "#endif\n"; - } - } - } # Add a trick_cast_as macro line for each class parsed in the file. These lines must appear at the bottom of the # file to ensure they are not in a namespace directive and they are after the #define statements they depend on. @@ -609,7 +590,8 @@ sub process_class($$$$$) { my $extracted ; my ($class_name) ; - my $template_typedefs ; + my @qualified_template_typedefs ; + my @unqualified_template_typedefs ; ## Extract the class_name from the class_string $class_string =~ /^(?:class|struct)\s+ # keyword class or struct @@ -692,10 +674,6 @@ sub process_class($$$$$) { my ($template_type_no_sp) = $template_full_type ; $template_type_no_sp =~ s/\s//g ; - # If the type is qualified, assume it's fully qualified and put the - # %template directive in the global namespace. - # See https://github.com/nasa/trick/issues/768 - my $qualified = $template_type_no_sp =~ /^\w+(::)\w+ 0x040000\n\%pythoncode \%{\n if '$s' in globals():\n $s.__setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n\%}\n#endif\n" ; } } - $$new_contents_ref .= $extracted . $tail ; + $$new_contents_ref .= $extracted . $tail . $my_struct_contents ; } diff --git a/share/trick/makefiles/trickify.mk b/share/trick/makefiles/trickify.mk index 9f2ab8c95..68aa90e87 100644 --- a/share/trick/makefiles/trickify.mk +++ b/share/trick/makefiles/trickify.mk @@ -115,7 +115,7 @@ TRICK_CFLAGS += $(TRICKIFY_CXX_FLAGS) TRICK_CXXFLAGS += $(TRICKIFY_CXX_FLAGS) # Ensure we can process all headers -TRICK_EXT_LIB_DIRS := +TRICK_EXT_LIB_DIRS := $(TRICKIFY_EXT_LIB_DIRS) .PHONY: all all: $(TRICKIFY_OBJECT_NAME) $(TRICKIFY_PYTHON_DIR) diff --git a/share/trick/trickops/MonteCarloGenerationHelper.py b/share/trick/trickops/MonteCarloGenerationHelper.py index 10d2c62a9..e6cf1c216 100644 --- a/share/trick/trickops/MonteCarloGenerationHelper.py +++ b/share/trick/trickops/MonteCarloGenerationHelper.py @@ -30,7 +30,6 @@ import sys, os -import send_hs import argparse, glob import subprocess, errno diff --git a/share/trick/trickops/send_hs.py b/share/trick/trickops/send_hs.py index 8404769f5..e885a046f 100644 --- a/share/trick/trickops/send_hs.py +++ b/share/trick/trickops/send_hs.py @@ -1,67 +1,254 @@ import re, os import pdb +# This global is the result of hours of frustration and debugging. This is only used by doctest +# but appears to be the only solution to the problem of __file__ not being an absolute path in +# some cases for some python versions and how that interacts with this class's os.chdir() when its +# base class initializes. If you attempt to define this_trick locally in the doctest block, +# which was my original attempt, you will find that the value of this_trick is different inside +# the __init__ doctest evaluation compared to any other member function. I believe this is only +# the case when using python version < 3.9, according to the information found here: +# https://note.nkmk.me/en/python-script-file-path/ +# I do not like adding globals to "production code" just to facilitate a testing mechanism, but +# I don't know of any cleaner way way to do this. -Jordan 12/2024 +this_trick = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../..')) class send_hs(object): """ - Reads a file containing the send_hs output and returns a send_hs - object containing the values from that output + Utility class for parsing simulation diagnostic data at the end of a + Trick-generated send_hs output file. """ - def __init__(self, hs_file): + def __init__(self, hs_file=None): + """ + Initialize this instance. + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + + Parameters + ---------- + hs_file : str + Path to the send_hs output to read and parse + """ self.hs_file = hs_file - self.actual_init_time = None - self.actual_elapsed_time = None - self.start_time = None - self.stop_time = None - self.elapsed_time = None - self.actual_cpu_time_used = None - self.sim_cpu_time = None - self.init_cpu_time = None - self.parse() - def parse(self): - f = open(self.hs_file, 'r') - lines = f.readlines() - for line in lines: - self.actual_init_time = self.attempt_hs_match('ACTUAL INIT TIME',self.actual_init_time, line) - self.actual_elapsed_time = self.attempt_hs_match('ACTUAL ELAPSED TIME',self.actual_elapsed_time, line) - self.start_time = self.attempt_hs_match('SIMULATION START TIME',self.start_time, line) - self.stop_time = self.attempt_hs_match('SIMULATION STOP TIME',self.stop_time, line) - self.elapsed_time = self.attempt_hs_match('SIMULATION ELAPSED TIME',self.elapsed_time, line) - self.actual_cpu_time_used = self.attempt_hs_match('ACTUAL CPU TIME USED',self.actual_cpu_time_used, line) - self.sim_cpu_time = self.attempt_hs_match('SIMULATION / CPU TIME',self.sim_cpu_time, line) - self.init_cpu_time = self.attempt_hs_match('INITIALIZATION CPU TIME',self.init_cpu_time, line) - # TODO add capture of blade and DIAGNOSTIC: Reached termination time as success criteria - - def attempt_hs_match(self, name, var, text): + self.num_lines = None + self._diagnostics = {} # Internal dict of diagnostic keys and values + self._missing_diagnostics = [] # List of diagnostics we failed to find + for k in self.keys(): + self._diagnostics[k] = None + + if hs_file: + self.parse() + + def is_valid(self): + """ + Check for validity of the parsed send_hs file. If any expected internal members + were not able to be parsed, store them in missing_diagnostics and return False. + If everything was found, return True + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.is_valid() + True + + Returns + ------- + True if all expected internal members were parsed. False if any member wasn't found. + + """ + self._missing_diagnostics = [] # Reset the internal list + for d in self._diagnostics: + if self._diagnostics[d] is None: + self._missing_diagnostics.append(d) + if self._missing_diagnostics: + return False + else: + return True + + def missing_diagnostics(self): + ''' + Check for validity and return a list of any missing diagnostics that we were + unable to find in the send_hs output + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.missing_diagnostics() + [] + + Returns + ------- + list + All diagnotics that were unable to be parsed + ''' + self.is_valid() + return self._missing_diagnostics + + def parse(self, hs_file=None): + ''' + Parse the content of self.hs_file and assign internal variables for each field found + if hs_file is given, overwrite self.hs_file with it's value + + + >>> sh = send_hs() + >>> sh.parse(os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh2 = send_hs() + >>> sh2.parse() + Traceback (most recent call last): + ... + RuntimeError: send_hs file: 'None' cannot be read. You must provide a valid send_hs output file. + + Parameters + ---------- + hs_file : str + Path to hs_file to parse. If None, self.hs_file is used instead. + + Raises + ------ + RuntimeError + If no send_hs file was provided at construction or when calling this function + ''' + if hs_file: + self.hs_file = hs_file + + if not self.hs_file or not os.path.isfile(self.hs_file): + raise RuntimeError(f"send_hs file: '{self.hs_file}' cannot be read." + " You must provide a valid send_hs output file.") + + self.num_lines = 0 + with open(self.hs_file) as fp: + # Efficiency guard - we don't do an re pattern match until we've starting parsing the diagnostic msg + start_reading_diagnostics = False + for line in fp: + self.num_lines += 1 + # NOTE this "attempt match" approach is less efficient but it should be + # robust to a future ordering change of the lines from Trick's output. + if start_reading_diagnostics: + for d in self._diagnostics: + if self._diagnostics[d] is None: + self._diagnostics[d] = self._attempt_hs_match(d, line) + # This text precedes the diagnostics output, use it as the trigger to start parsing + if 'REALTIME SHUTDOWN STATS:' in line: + start_reading_diagnostics = True + continue + # Set validity status immediately after parsing + self.is_valid() + + def _attempt_hs_match(self, name, text): """ - name: pattern to match (e.g. SIMULATION START TIME) - var: variable to assign value if match found - text: text to search for pattern - returns: var if not found, found value if found + Parameters + ---------- + hs_file : str + Path to hs_file to parse. If None, self.hs_file is used instead. + name : str + Pattern to match (e.g. SIMULATION START TIME) + text : str + Text to search for pattern within + + Returns + ---------- + float or None + Value of name if found, else: None """ + name = name.replace('(', '\(').replace(')', '\)') m = re.match(name+': +([-]?[0-9]*\.?[0-9]+)', text.strip()) if m: return(float(m.group(1))) - return(var) - - def get(self,name): - """ - Get a value by the name that appears in the send_hs message - """ - if 'ACTUAL INIT TIME' in name: - return self.actual_init_time - if 'ACTUAL ELAPSED TIME' in name: - return self.actual_elapsed_time - if 'SIMULATION START TIME' in name: - return self.start_time - if 'SIMULATION STOP TIME' in name: - return self.stop_time - if 'SIMULATION ELAPSED TIME' in name: - return self.elapsed_time - if 'ACTUAL CPU TIME USED' in name: - return self.actual_cpu_time_used - if 'SIMULATION / CPU TIME' in name: - return self.sim_cpu_time - if 'INITIALIZATION CPU TIME' in name: - return self.init_cpu_time - else: - return None + else: + return(None) + + def keys(self) -> list: + """ + Return a list of all possible keys/names that get() can accept This is + the master list of all diagnostics we search for when we parse the send_hs + file, and naming should match 1:1 with the output of a Trick simulation + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.keys() #doctest: +ELLIPSIS + ['ACTUAL INIT TIME', ... 'INVOLUNTARY CONTEXT SWITCHES (RUN)'] + + Returns + ------- + list + All possible diagnostic names that get() can accept + """ + return (['ACTUAL INIT TIME', + 'ACTUAL ELAPSED TIME', + 'SIMULATION START TIME', + 'SIMULATION STOP TIME', + 'SIMULATION ELAPSED TIME', + 'USER CPU TIME USED', + 'SYSTEM CPU TIME USED', + 'SIMULATION / CPU TIME', + 'INITIALIZATION USER CPU TIME', + 'INITIALIZATION SYSTEM CPU TIME', + 'SIMULATION RAM USAGE', + 'VOLUNTARY CONTEXT SWITCHES (INIT)', + 'INVOLUNTARY CONTEXT SWITCHES (INIT)', + 'VOLUNTARY CONTEXT SWITCHES (RUN)', + 'INVOLUNTARY CONTEXT SWITCHES (RUN)' + ]) + + def get_diagnostic (self, name: str) -> float: + """ + Get a diagnostic value by it's name or partial name + The first name matched in the self._diagnostics dict will be returned + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.get_diagnostic('ACTUAL INIT TIME') + 42.606 + >>> sh.get_diagnostic('SIMULATION RAM USAGE') + 1198.867 + + Parameters + ---------- + name : str + Name or partial name of diagnostic to retrieve + + Returns + ------- + float + Value of diagnostic name given + + Raises + ------ + LookupError + If name cannot be found in self._diagnostics + """ + + for d in self._diagnostics: + if name in d: + return(self._diagnostics[d]) + raise LookupError(f"Unable to get diagnostic '{name}'. Is it spelled correctly?") + + def get(self, name: str) -> float: + """ + Get a diagnostic value by it's name or partial name. Convienence function that + calls self.get() directly. + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.get('ACTUAL INIT TIME') + 42.606 + >>> sh.get('SIMULATION RAM USAGE') + 1198.867 + + Returns + ------- + float + Value of diagnostic name given + """ + return self.get_diagnostic(name) + + @property + def diagnostics (self) -> dict: + return dict(self._diagnostics) + + def get_diagnostics (self) -> dict: + """ + Get all diagnostics stored in internal self._diagnostics dictionary + + >>> sh = send_hs(hs_file=os.path.join(this_trick,"share/trick/trickops/tests/send_hs.nominal")) + >>> sh.get_diagnostics() #doctest: +ELLIPSIS + {'ACTUAL INIT TIME': 42.606, 'ACTUAL ELAPSED TIME': 55.551, ... 14.0} + + Returns + ------- + dict + A copy of the internal self._diagnostics dictionary + """ + return (self.diagnostics) diff --git a/share/trick/trickops/tests/run_tests.py b/share/trick/trickops/tests/run_tests.py index 35f178130..491c722cc 100755 --- a/share/trick/trickops/tests/run_tests.py +++ b/share/trick/trickops/tests/run_tests.py @@ -34,7 +34,7 @@ def run_tests(args): # Run all doc tests by eating our own dogfood doctest_files = ['TrickWorkflow.py', 'WorkflowCommon.py', 'TrickWorkflowYamlVerifier.py', - 'MonteCarloGenerationHelper.py'] + 'MonteCarloGenerationHelper.py', 'send_hs.py'] wc = WorkflowCommon(this_dir, quiet=True) jobs = [] log_prepend = '_doctest_log.txt' diff --git a/share/trick/trickops/tests/send_hs.missing_all b/share/trick/trickops/tests/send_hs.missing_all new file mode 100644 index 000000000..362b537b6 --- /dev/null +++ b/share/trick/trickops/tests/send_hs.missing_all @@ -0,0 +1,5 @@ +|L 3|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Failed to resolve parameter 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' +|L 2|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Mismatched data types for 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' -> 'another_structure.myvariable' +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.flexMode. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.myarray[0]. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable foo.bar.yippy.skippy.axial_sep_distance. diff --git a/share/trick/trickops/tests/send_hs.missing_some b/share/trick/trickops/tests/send_hs.missing_some new file mode 100644 index 000000000..69ce40652 --- /dev/null +++ b/share/trick/trickops/tests/send_hs.missing_some @@ -0,0 +1,26 @@ +|L 3|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Failed to resolve parameter 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' +|L 2|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Mismatched data types for 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' -> 'another_structure.myvariable' +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.flexMode. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.myarray[0]. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable foo.bar.yippy.skippy.axial_sep_distance. +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| + REALTIME SHUTDOWN STATS: + ACTUAL INIT TIME: 32.306 + ACTUAL ELAPSED TIME: 35.551 +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| +SIMULATION TERMINATED IN + PROCESS: 0 + ROUTINE: Executive_loop_single_thread.cpp:98 + DIAGNOSTIC: Reached termination time + + SIMULATION START TIME: -10.000 + SIMULATION STOP TIME: 78.955 + SIMULATION ELAPSED TIME: 88.955 + SYSTEM CPU TIME USED: 3.235 + SIMULATION / CPU TIME: 9.218 + INITIALIZATION USER CPU TIME: 72.763 + SIMULATION RAM USAGE: 193.867MB + (External program RAM usage not included!) + VOLUNTARY CONTEXT SWITCHES (INIT): 292 + VOLUNTARY CONTEXT SWITCHES (RUN): 37 + INVOLUNTARY CONTEXT SWITCHES (RUN): 17 diff --git a/share/trick/trickops/tests/send_hs.nominal b/share/trick/trickops/tests/send_hs.nominal new file mode 100644 index 000000000..44959cb0b --- /dev/null +++ b/share/trick/trickops/tests/send_hs.nominal @@ -0,0 +1,29 @@ +|L 3|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Failed to resolve parameter 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' +|L 2|2024/11/21,15:54:06|myworkstation| |T 0|0.000000| TrickParamCopy(UserCodeExample): Mismatched data types for 'fsw_out.fswOut_block.fswOut.fsw1HzOut.domain.subdomain.variable' -> 'another_structure.myvariable' +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.flexMode. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable flex.flex_Obj.myarray[0]. +|L 2|2024/11/21,15:54:07|myworkstation| |T 0|0.000000| Could not find Data Record variable foo.bar.yippy.skippy.axial_sep_distance. +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| + REALTIME SHUTDOWN STATS: + ACTUAL INIT TIME: 42.606 + ACTUAL ELAPSED TIME: 55.551 +|L 0|2024/11/21,15:54:20|myworkstation| |T 0|68.955000| +SIMULATION TERMINATED IN + PROCESS: 0 + ROUTINE: Executive_loop_single_thread.cpp:98 + DIAGNOSTIC: Reached termination time + + SIMULATION START TIME: 0.000 + SIMULATION STOP TIME: 68.955 + SIMULATION ELAPSED TIME: 68.955 + USER CPU TIME USED: 55.690 + SYSTEM CPU TIME USED: 0.935 + SIMULATION / CPU TIME: 1.218 + INITIALIZATION USER CPU TIME: 42.783 + INITIALIZATION SYSTEM CPU TIME: 0.901 + SIMULATION RAM USAGE: 1198.867MB + (External program RAM usage not included!) + VOLUNTARY CONTEXT SWITCHES (INIT): 792 +INVOLUNTARY CONTEXT SWITCHES (INIT): 187 + VOLUNTARY CONTEXT SWITCHES (RUN): 97 + INVOLUNTARY CONTEXT SWITCHES (RUN): 14 diff --git a/share/trick/trickops/tests/test.py b/share/trick/trickops/tests/test.py index 8568bcc86..c301de998 100644 --- a/share/trick/trickops/tests/test.py +++ b/share/trick/trickops/tests/test.py @@ -5,6 +5,7 @@ import os, sys, pdb import unittest +import ut_send_hs import ut_WorkflowCommon import ut_TrickWorkflowYamlVerifier import ut_TrickWorkflow @@ -14,6 +15,7 @@ def load_tests(*args): passed_args = locals() suite = unittest.TestSuite() + suite.addTests(ut_send_hs.suite()) suite.addTests(ut_TrickWorkflowYamlVerifier.suite()) suite.addTests(ut_TrickWorkflow.suite()) suite.addTests(ut_WorkflowCommon.suite()) @@ -23,6 +25,7 @@ def load_tests(*args): # Local module level execution only if __name__ == '__main__': suites = unittest.TestSuite() + suites.addTests(ut_send_hs.suite()) suites.addTests(ut_TrickWorkflowYamlVerifier.suite()) suites.addTests(ut_TrickWorkflow.suite()) suites.addTests(ut_WorkflowCommon.suite()) diff --git a/share/trick/trickops/tests/ut_send_hs.py b/share/trick/trickops/tests/ut_send_hs.py new file mode 100644 index 000000000..08643f5e4 --- /dev/null +++ b/share/trick/trickops/tests/ut_send_hs.py @@ -0,0 +1,216 @@ +import os, sys, glob +import unittest, shutil +import pdb +from testconfig import this_trick, tests_dir +import send_hs +def suite(): + """Create test suite from test cases here and return""" + suites = [] + suites.append(unittest.TestLoader().loadTestsFromTestCase(SendHsTestCase)) + suites.append(unittest.TestLoader().loadTestsFromTestCase(SendHsNoFileTestCase)) + suites.append(unittest.TestLoader().loadTestsFromTestCase(SendHsMissingAllTestCase)) + suites.append(unittest.TestLoader().loadTestsFromTestCase(SendHsMissingSomeTestCase)) + suites.append(unittest.TestLoader().loadTestsFromTestCase(SendHsVerifyDiagNames)) + return (suites) + +class SendHsTestCase(unittest.TestCase): + + def setUp(self): + # Nominal - reading a valid send_hs output file + self.instance = send_hs.send_hs(hs_file=os.path.join(tests_dir, 'send_hs.nominal')) + self.assertEqual(self.instance.missing_diagnostics(), []) + + def tearDown(self): + if self.instance: + del self.instance + self.instance = None + + def assert_values(self, instance): + self.assertEqual(instance.hs_file, os.path.join(tests_dir, 'send_hs.nominal')) + self.assertEqual(instance.num_lines, 29) + # Test the values of each individual diagnostic name + self.assertEqual(instance.get('ACTUAL INIT TIME'), 42.606) + self.assertEqual(instance.get('ACTUAL INIT'), 42.606) # Partial name test + self.assertEqual(instance.get('ACTUAL ELAPSED TIME'), 55.551) + self.assertEqual(instance.get('SIMULATION START TIME'), 0.0) + self.assertEqual(instance.get('SIMULATION STOP TIME'), 68.955) + self.assertEqual(instance.get('SIMULATION ELAPSED TIME'), 68.955) + self.assertEqual(instance.get('USER CPU TIME USED'), 55.690 ) + self.assertEqual(instance.get('SYSTEM CPU TIME USED'), 0.935) + self.assertEqual(instance.get('SIMULATION / CPU TIME'), 1.218) + self.assertEqual(instance.get('INITIALIZATION USER CPU TIME'), 42.783 ) + self.assertEqual(instance.get('INITIALIZATION SYSTEM CPU TIME'), 0.901) + self.assertEqual(instance.get('SIMULATION RAM USAGE'), 1198.867) + self.assertEqual(instance.get('VOLUNTARY CONTEXT SWITCHES (INIT)'), 792) + self.assertEqual(instance.get('VOLUNTARY CONTEXT SWITCHES'), 792) # Partial name test + self.assertEqual(instance.get('INVOLUNTARY CONTEXT SWITCHES (INIT)'), 187) + self.assertEqual(instance.get('VOLUNTARY CONTEXT SWITCHES (RUN)'), 97) + self.assertEqual(instance.get('INVOLUNTARY CONTEXT SWITCHES (RUN)'), 14 ) + self.assertEqual(instance.is_valid(), True) + self.assertEqual(instance.missing_diagnostics(), []) + + # Test the values of the full dict + diags = self.instance.get_diagnostics() + self.assertEqual(diags['ACTUAL INIT TIME'], 42.606) + self.assertEqual(diags['ACTUAL ELAPSED TIME'], 55.551) + self.assertEqual(diags['SIMULATION START TIME'], 0.0) + self.assertEqual(diags['SIMULATION STOP TIME'], 68.955) + self.assertEqual(diags['SIMULATION ELAPSED TIME'], 68.955) + self.assertEqual(diags['USER CPU TIME USED'], 55.690 ) + self.assertEqual(diags['SYSTEM CPU TIME USED'], 0.935) + self.assertEqual(diags['SIMULATION / CPU TIME'], 1.218) + self.assertEqual(diags['INITIALIZATION USER CPU TIME'], 42.783 ) + self.assertEqual(diags['INITIALIZATION SYSTEM CPU TIME'], 0.901) + self.assertEqual(diags['SIMULATION RAM USAGE'], 1198.867) + self.assertEqual(diags['VOLUNTARY CONTEXT SWITCHES (INIT)'], 792) + self.assertEqual(diags['INVOLUNTARY CONTEXT SWITCHES (INIT)'], 187) + self.assertEqual(diags['VOLUNTARY CONTEXT SWITCHES (RUN)'], 97) + self.assertEqual(diags['INVOLUNTARY CONTEXT SWITCHES (RUN)'], 14 ) + + # Test the other way to get diagnostics and ensure we get the same result + diags2 = self.instance.diagnostics + self.assertEqual(diags, diags2 ) + + + def test_get_unknown_diag(self): + with self.assertRaises(LookupError): + self.instance.get('fake_diag_name') + + def test_init(self): + # Init happened in setUp, make sure values are right + self.assert_values(self.instance) + + def test_twostep_init(self): + # Construct without giving the instance a file to parse + self.tsi = send_hs.send_hs() + # Everything is None before the file has been parsed + for d in self.tsi._diagnostics: + self.assertEqual(self.tsi._diagnostics[d], None) + # Parse the file after construction + self.tsi = send_hs.send_hs('send_hs.nominal') + # Make sure values are right + self.assert_values(self.instance) + +class SendHsNoFileTestCase(unittest.TestCase): + def setUp(self): + # No-file constructions + self.instance = send_hs.send_hs() + + def test_init(self): + with self.assertRaises(RuntimeError): + self.instance.parse() # Nothing to parse, no file given + +class SendHsMissingAllTestCase(unittest.TestCase): + + def setUp(self): + # Invalid - reading a send_hs file missing all expected output + self.instance = send_hs.send_hs(hs_file=os.path.join(tests_dir, 'send_hs.missing_all')) + + def tearDown(self): + if self.instance: + del self.instance + self.instance = None + + def test_init(self): + self.assertEqual(self.instance.num_lines, 5) + self.assertEqual(self.instance.is_valid(), False) + self.assertEqual(self.instance.missing_diagnostics(), [ + 'ACTUAL INIT TIME', + 'ACTUAL ELAPSED TIME', + 'SIMULATION START TIME', + 'SIMULATION STOP TIME', + 'SIMULATION ELAPSED TIME', + 'USER CPU TIME USED', + 'SYSTEM CPU TIME USED', + 'SIMULATION / CPU TIME', + 'INITIALIZATION USER CPU TIME', + 'INITIALIZATION SYSTEM CPU TIME', + 'SIMULATION RAM USAGE', + 'VOLUNTARY CONTEXT SWITCHES (INIT)', + 'INVOLUNTARY CONTEXT SWITCHES (INIT)', + 'VOLUNTARY CONTEXT SWITCHES (RUN)', + 'INVOLUNTARY CONTEXT SWITCHES (RUN)' + ]) + +class SendHsMissingSomeTestCase(unittest.TestCase): + + def setUp(self): + # Invalid - reading a send_hs file missing all expected output + self.instance = send_hs.send_hs(hs_file=os.path.join(tests_dir, 'send_hs.missing_some')) + + def tearDown(self): + if self.instance: + del self.instance + self.instance = None + + def test_init(self): + self.assertEqual(self.instance.num_lines, 26) + self.assertEqual(self.instance.is_valid(), False) + self.assertEqual(self.instance.missing_diagnostics(), [ + 'USER CPU TIME USED', + 'INITIALIZATION SYSTEM CPU TIME', + 'INVOLUNTARY CONTEXT SWITCHES (INIT)', + ]) + self.assertEqual(self.instance.get('ACTUAL INIT TIME'), 32.306) + self.assertEqual(self.instance.get('ACTUAL INIT'), 32.306) # Partial name test + self.assertEqual(self.instance.get('ACTUAL ELAPSED TIME'), 35.551) + self.assertEqual(self.instance.get('SIMULATION START TIME'), -10.00) + self.assertEqual(self.instance.get('SIMULATION STOP TIME'), 78.955) + self.assertEqual(self.instance.get('SIMULATION ELAPSED TIME'), 88.955) + self.assertEqual(self.instance.get('SYSTEM CPU TIME USED'), 3.235) + self.assertEqual(self.instance.get('SIMULATION / CPU TIME'), 9.218) + self.assertEqual(self.instance.get('INITIALIZATION USER CPU TIME'), 72.763 ) + self.assertEqual(self.instance.get('SIMULATION RAM USAGE'), 193.867) + self.assertEqual(self.instance.get('VOLUNTARY CONTEXT SWITCHES (INIT)'), 292) + self.assertEqual(self.instance.get('VOLUNTARY CONTEXT SWITCHES'), 292) # Partial name test + self.assertEqual(self.instance.get('VOLUNTARY CONTEXT SWITCHES (RUN)'), 37) + self.assertEqual(self.instance.get('INVOLUNTARY CONTEXT SWITCHES (RUN)'), 17 ) + + +class SendHsVerifyDiagNames(unittest.TestCase): + ''' + This test is bonkers. Here we search for the keys in the self._diagnostics (which + contain the text patterns we expect in the send_hs message) in the tracked source + code of Trick. This test should fail if the diagnostics are ever changed in name. + + The other way to do this would be to ingest a real send_hs generated from a + sim built and run before these tests run, but that adds significant complexity + and a new dependency to this unit testing -Jordan 12/2024 + ''' + + def setUp(self): + # An empty instance is all we need since the diag keys are baked into the class + self.instance = send_hs.send_hs() + self.files_to_search = [ + os.path.join(this_trick, "trick_source/sim_services/Executive/Executive_shutdown.cpp"), + os.path.join(this_trick, "trick_source/sim_services/RealtimeSync/RealtimeSync.cpp"), + ] + self.the_code = [] + for source_file in self.files_to_search: + with open(source_file, 'r') as f: + self.the_code += f.readlines() + + def tearDown(self): + if self.instance: + del self.instance + self.instance = None + + def test_verify_diags(self): + # Look for the diagnostic text in the files to search, and note + # that they are FOUND if they are + for d in self.instance.keys(): + if self.instance._diagnostics[d] == 'FOUND': + continue + for line in self.the_code: + if d in line: + self.instance._diagnostics[d] = 'FOUND' + break + diags = self.instance.diagnostics + # Assert that all expected text was found in the source code + for d in diags: + self.assertEqual(diags[d], 'FOUND') + + + + + diff --git a/test/SIM_swig_template_scoping/S_overrides.mk b/test/SIM_swig_template_scoping/S_overrides.mk index 09fe4a9f2..e1f6cccd6 100644 --- a/test/SIM_swig_template_scoping/S_overrides.mk +++ b/test/SIM_swig_template_scoping/S_overrides.mk @@ -1,3 +1,2 @@ -TRICK_CONVERT_SWIG_FLAGS := -s TRICK_CFLAGS += -Imodels TRICK_CXXFLAGS += -Imodels diff --git a/test/SIM_swig_template_scoping/models/Classes.hh b/test/SIM_swig_template_scoping/models/Classes.hh index df1f041a0..48ac93385 100644 --- a/test/SIM_swig_template_scoping/models/Classes.hh +++ b/test/SIM_swig_template_scoping/models/Classes.hh @@ -1,93 +1,185 @@ -#include +// @trick_parse{everything} -namespace a { - template class A { - private: - void operator=(const A&); +#ifndef Classes_HH +#define Classes_HH + +template +class Foo {}; + +namespace peer { + template + class Bar {}; + + struct Leek { + template + class Eggplant {}; }; + + namespace nested { + template + class Broccoli {}; + + struct Carrot { + template + class Celery {}; + }; + } } -namespace b { - - template class A {}; - template class B {}; - - class C { - public: - std::vector v; - a::A a; - a::A a2; - A a3; - A a4; - B b; - B b2; - b::B b3; - b::B b4; - B > ba; - B > ba2; - b::B > ba3; - b::B > ba4; - B > ba5; - B > ba6; - - // These don't work. Because the type is qualified, convert_swig assumes - // it's fully qualified and puts the %template directive in the global - // namespace. However, there is no A in the global namespace, so the wrapper - // code fails to compile. Bonus points if you can fix this without breaking - // something else. - //b::B > ba7; - //b::B > ba8; - }; +namespace prime { + + template + class Potato {}; - // This class is the same as the previous. Seems weird, but it reveals scoping - // errors under SWIG 2. The replication is not necessary in SWIG 4, which has - // better error detection. - class D { - public: - std::vector v; - a::A a; - a::A a2; - A a3; - A a4; - B b; - B b2; - b::B b3; - b::B b4; - B > ba; - B > ba2; - b::B > ba3; - b::B > ba4; - B > ba5; - B > ba6; - //b::B > ba7; - //b::B > ba8; + struct Turnip { + template + class Radish {}; }; -} -class E { - public: - std::vector v; - a::A a; - a::A a2; - b::B b3; - b::B b4; - b::B > ba3; - b::B > ba4; -}; - -namespace a { - class B { - public: - std::vector v; - a::A a; - a::A a2; - A a3; - A a4; - b::B b3; - b::B b4; - b::B > ba3; - b::B > ba4; - //b::B > ba7; - //b::B > ba8; + namespace nested { + template + class Baz {}; + + struct Artichoke { + template + class Asparagus {}; + }; + } + + struct Onion { +// ======================= WORKING ============================================= + /** + * We assume a qualified template instantiation is fully qualified + * starting from the global namespace, and we put the corresponding + * %template directive in the global namespace, which works if our + * assumption is correct. + */ + ::Foo fooInt; + + peer::Bar barInt; + ::peer::Bar barDouble; + + peer::Leek::Eggplant eggplantInt; + ::peer::Leek::Eggplant eggplantDouble; + + peer::nested::Broccoli broccoliInt; + ::peer::nested::Broccoli broccoliDouble; + + peer::nested::Carrot::Celery celeryInt; + ::peer::nested::Carrot::Celery celeryDouble; + + prime::Potato potatoInt; + ::prime::Potato potatoDouble; + + prime::Turnip::Radish radishInt; + ::prime::Turnip::Radish radishDouble; + + prime::nested::Baz bazInt; + ::prime::nested::Baz bazDouble; + + prime::nested::Artichoke::Asparagus asparagusint; + ::prime::nested::Artichoke::Asparagus asparagusDouble; + + /** + * We put the %template directive for unqualified template + * instantiations in the namespace containing the current class, + * which only works if the template is defined in that namespace as + * well. + */ + Potato potatoBool; + +// ======================= BROKEN ============================================== + /** + * Because Foo is declared in the global namespace, its %template + * directive must be in that scope as well. However, because the + * template instation here is unqualified, we put the %template + * directive in the containing namespace, which is an error. + * + * Error: 'Foo' resolves to '::Foo' and was incorrectly + * instantiated in scope 'prime' instead of within scope ''. + */ +// Foo fooDouble; + + /** + * Because these template instantiations are qualified, their %template + * directives are placed in the global namespace. However, because they + * are not _fully_ qualified, the generated SWIG input code is invalid. + * For partially-qualified instantiations, the directive should either be: + * 1. Declared in the global namespace and qualified with prime:: + * 2. Declared in the prime namespace + * + * We don't track enough information to do either. + * + * Error: Template 'Turnip::Radish' undefined. + * Error: Template 'nested::Baz' undefined. + * Error: Template 'nested::Artichoke::Asparagus' undefined. + */ +// Turnip::Radish radishBool; +// nested::Baz bazBool; +// nested::Artichoke::Asparagus asparagusBool; + + template + class InnerOnion {}; // onions have layers! + + /** + * All %template directives are placed before the class containing their + * corresponding template instantiations. For member templates + * (templates declared within the class being processed), this results + * in the %template directive referencing the member template before it + * has been declared, which is an error. + * + * Error: Template 'InnerOnion' undefined. + * Error: Template 'Onion::InnerOnion' undefined. + * Error: Template 'prime::Onion::InnerOnion' undefined. + * Error: Template '::prime::Onion::InnerOnion' undefined. + */ +// InnerOnion layer1; +// Onion::InnerOnion layer2; +// prime::Onion::InnerOnion layer2; +// ::prime::Onion::InnerOnion layer4; }; + + // Same tests as above, but within a nested namespace. + namespace nested { + class Corn { + ::Foo fooLeek; + + peer::Bar > barFoo; + ::peer::Bar barLeek; + + peer::Leek::Eggplant eggplantLong; + ::peer::Leek::Eggplant eggplantLeek; + + peer::nested::Broccoli broccoliLong; + ::peer::nested::Broccoli broccoliLeek; + + peer::nested::Carrot::Celery celeryLong; + ::peer::nested::Carrot::Celery celeryLeek; + + prime::Potato potatoLong; + ::prime::Potato potatoLeek; + + prime::Turnip::Radish radishLong; + ::prime::Turnip::Radish radishLeek; + + prime::nested::Baz bazLong; + ::prime::nested::Baz bazLeek; + + prime::nested::Artichoke::Asparagus asparagusLong; + ::prime::nested::Artichoke::Asparagus asparagusLeek; + + // These fail in Onion, but work here because Onion has already been processed. + prime::Onion::InnerOnion onionLong; + ::prime::Onion::InnerOnion onionLeek; + + // These fail for the same reason as in Onion. +// Foo fooFloat; +// Turnip::Radish radishFloat; +// nested::Baz bazFloat; +// nested::Artichoke::Asparagus asparagusFloat; +// Onion::InnerOnion innerOnionFloat; + }; + } } + +#endif diff --git a/test/SIM_test_dr/RUN_test/unit_test.py b/test/SIM_test_dr/RUN_test/unit_test.py index eaeac8218..2d3cf3a52 100644 --- a/test/SIM_test_dr/RUN_test/unit_test.py +++ b/test/SIM_test_dr/RUN_test/unit_test.py @@ -1,10 +1,59 @@ -exec(open("Modified_data/dr_typesASCII.dr").read()) -exec(open("Modified_data/dr_typesBINARY.dr").read()) -exec(open("Modified_data/dr_bitfASCII.dr").read()) -exec(open("Modified_data/dr_bitfBINARY.dr").read()) +from trick.unit_test import * -trick_utest.unit_tests.enable() ; -trick_utest.unit_tests.set_file_name( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_dr.xml" ) ; +trick_utest.unit_tests.enable() +trick_utest.unit_tests.set_file_name( os.getenv("TRICK_HOME") + "/trick_test/SIM_test_dr.xml" ) +trick_utest.unit_tests.set_test_name( "DRTest" ) + +###################################################################################################################### + +test_suite = "drg api" + +# Get the number of data recording groups before any drg is created +num_drgs = trick.get_num_data_record_groups() + +# Check the result of trick.get_num_data_record_groups() +TRICK_EXPECT_EQ( num_drgs , 0 , test_suite , "0 drgs before any created" ) + +# The first item of each pair is the .dr file name and the second item of each pair is the drg name +dr_file_name_drg_name_tuple = (('Modified_data/dr_typesASCII.dr', 'DR_typesASCII'), + ('Modified_data/dr_typesBINARY.dr', 'DR_typesBINARY'), + ('Modified_data/dr_bitfASCII.dr', 'DR_bitfieldsASCII'), + ('Modified_data/dr_bitfBINARY.dr', 'DR_bitfieldsBINARY')) + +num_files = len(dr_file_name_drg_name_tuple) +for i in range(num_files): + exec(open(dr_file_name_drg_name_tuple[i][0]).read()) + +# Get the number of data recording groups created +num_drgs = trick.get_num_data_record_groups() + +# Check the result of trick.get_num_data_record_groups() +TRICK_EXPECT_EQ( num_drgs , 4 , test_suite , "num of dr groups = 4" ) + +# Test trick.get_data_record_group() for getting the drg pointer by its name +# Check the name of the obtained drg instead of the drg pointer +for i in range(num_drgs): + TRICK_EXPECT_EQ( trick.get_data_record_group(dr_file_name_drg_name_tuple[i][1]).get_group_name(), dr_file_name_drg_name_tuple[i][1], test_suite , "get drg by name " + dr_file_name_drg_name_tuple[i][1] ) + +# Test trick.get_data_record_group_by_idx(= 0: + DR_GROUP_ID += 1 +except NameError: + DR_GROUP_ID = 0 + drg = [] + +drg.append(trick.DRBinary("StateData")) +drg[DR_GROUP_ID].set_freq(trick.DR_Always) +drg[DR_GROUP_ID].set_cycle(0.10) +drg[DR_GROUP_ID].set_single_prec_only(False) +drg[DR_GROUP_ID].add_variable("dyn.body.force[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.force[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.force[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle_force[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle_force[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle_force[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[0][0]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[0][1]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[0][2]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[1][0]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[1][1]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[1][2]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[2][0]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[2][1]") +drg[DR_GROUP_ID].add_variable("dyn.body.rotate[2][2]") +drg[DR_GROUP_ID].add_variable("dyn.body.pos[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.pos[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.pos[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.angle[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.vel[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.vel[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.vel[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.acc[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.acc[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.acc[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.omega[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.omega[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.omega[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.omegaDot[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.omegaDot[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.omegaDot[2]") +drg[DR_GROUP_ID].add_variable("dyn.body.FORCE_INIT[0]") +drg[DR_GROUP_ID].add_variable("dyn.body.FORCE_INIT[1]") +drg[DR_GROUP_ID].add_variable("dyn.body.FORCE_INIT[2]") + + + +trick.add_data_record_group(drg[DR_GROUP_ID], trick.DR_Buffer) +drg[DR_GROUP_ID].enable() diff --git a/trick_sims/SIM_singlerigidbody/README.md b/trick_sims/SIM_singlerigidbody/README.md new file mode 100644 index 000000000..8de869061 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/README.md @@ -0,0 +1,122 @@ +# Single Rigid Body +``SIM_singlerigidbody`` is a simulation of the motion of a sphere that is a single rigid body with six degrees of freedom. + +The motion of the sphere is found by using Euler's equations of motion, and can be controlled by changing several initial input values such as force, position, angle, angular velocity, velocity, and position of the force. The recorded data of the motion can then be viewed and analyzed. + +## Dynamics model + +### Equations of Motion +To determine the sphere's motion, we will use Euler's equations of motion for a single rigid body. Euler's equations describe the combined translational and rotational dynamics of a rigid body in three-dimensional space. + +![Equations_of_motion](images\EquationsofMotion.png) + +Where, + +![Force_and_Torque_eq](images\ForceTorque.png) +* is a 6x1 column vector. +* F is the applied force. +* τ is the torque. + +![Mass_Matrix_eq](images\MassMatrix.png) +* is a 6x6 matrix. +* m is the mass. +* I 3x3 is the identity matrix. +* I is the inertia. +* C m is the center of mass. + +![Acceleration_Omega](images\AccOmega.png) +* is a 6x1 column vector. +* a o is the acceleration. + +![Force_Torque_Remain](images\ForceTorqueRemain.png) +* is a 6x1 column vector. +* ω A is the angular velocity. + + +#### Force +The force acting on the sphere is calculated by the cross product of the rotation matrix and initial force. + +![Force](images\Force.png) + +#### Torque +Torque is found by the cross product of the rotation matrix and the cross product of the position of force and initial force. + +![Torque](images\Torque.png) + +#### Mass +The default value of the mass of the sphere is: + +![Mass](images\Mass.png) + +#### Radius +The default value of the radius is: + +![Radius](images\Radius.png) + +#### Center of mass +The center of mass of a sphere is equal to its radius. + +![Center_of_mass](images\Centerofmass.png) + +#### Inertia +Inertia of a sphere is calculated by: + +![Inertia](images\Inertia.png) + +#### Acceleration and angular acceleration +To determine the acceleration and angular acceleration, ```dLU_Choleski``` must be used. ```dLU_Choleski``` solves linear sets of equations in the form of [A]x = b and returns the vector x, which is the acceleration. It decomposes the symmetric, positive definite [A] matrix into a lower triangular form, where we can then substitute and our equation becomes: + +![Vector_a](images\Vectora.png) + +The term 'a' is a 6x1 vector. Acceleration is equal to the first three values of vector a and angular acceleration is equal to the last three values of vector a. + +### State Integration +The state is defined by position, velocity, angle, and angular velocity. They are calculated by numerically integrating velocity, acceleration, angular velocity, and angular acceleration, respectively. + +#### Position +The position of the sphere is determined by integrating the velocity over time. + +![Position](images\Position.png) + +#### Velocity +The velocity is determined by integrating the acceleration of the sphere over time. + +![Velocity](images\Velocity.png) + +#### Angle +The angle is determined by integrating the angular velocity over time. + +![Angle](images\Angle.png) + +#### Angular velocity +The angular velocity is determined by integrating the angular acceleration over time. + +![Angular_Velocity](images\Angularvelocity.png) + + + +## Inputs + +Variable |Type |Units +----------------------------|-------------|--------- +dyn.body.POS_INIT |double[3] |m +dyn.body.ANGLE_INIT |double[3] |rad +dyn.body.OMEGA_INIT |double[3] |rad*s^-1 +dyn.body.FORCE_INIT |double[3] |N +dyn.body.TORQUE_INIT |double[3] |N*m +dyn.body.VEL_INIT |double[3] |m/s +dyn.body.POS_FORCE |double[3] |m + +## Outputs + +Variable |Type |Units +----------------------------|-------------|-------- +dyn.body.pos |double[3] |m +dyn.body.vel |double[3] |m/s +dyn.body.acc |double[3] |m/s^2 +dyn.FORCE_INIT |double[3] |N +dyn.force |double[3] |N +dyn.body.angle_force |double[3] |rad +dyn.body.omegaDot |double[3] |rad/^s2 +dyn.body.omega |double[3] |rad*s^-1 +dyn.body.rotate |double[3] | diff --git a/trick_sims/SIM_singlerigidbody/RUN_test/input.py b/trick_sims/SIM_singlerigidbody/RUN_test/input.py new file mode 100644 index 000000000..b4e8e7395 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/RUN_test/input.py @@ -0,0 +1,81 @@ +################################################################################ +# _ ___ _ # +# / |___| _ ) ___ __| |_ _ # +# | |___| _ \/ _ \/ _` | || | # +# |_| |___/\___/\__,_|\_, | # +# __ ___ ___ __|__/ # +# / / ___| \ / _ \| __| # +# / _ \___| |) | (_) | _| # +# \___/ |___/ \___/|_| # +# # +# This input creates a simple body with 6 DOF: # +# -Creates 1 body # +# -Initializes 6 DOF # +################################################################################ + +# ============================================================================ - +# This file is used to store several quality of life python functions. +# ============================================================================ - +exec(open("Modified_data/state_data.dr").read()) +exec(open("Modified_data/realtime.py").read()) + +################################################################################ +# dyn DATA # +# (NOTE: dyn is our instance of BodySimObject) # +# This builds our objects, and defines their physical properties. # +################################################################################ + +# ============================================================================ - +# Establish the physical properties of the body...CONSTANTS! +# ============================================================================ - +dyn.body.mass = 20.0 +dyn.body.radius = 2.0 + +# ============================================================================ - +# Initialize system +# ============================================================================ - +M_PI = 3.141592653589793238460 + +#dyn.body.POS_INIT[0] = 0.0 +#dyn.body.POS_INIT[1] = 0.0 +#dyn.body.POS_INIT[2] = 0.0 + +#dyn.body.ANGLE_INIT[0] = 0.0 +#dyn.body.ANGLE_INIT[1] = 0.0 +#dyn.body.ANGLE_INIT[2] = 0.0 + +#dyn.body.OMEGA_INIT[0] = 0.0 +#dyn.body.OMEGA_INIT[1] = 0.0 +#dyn.body.OMEGA_INIT[2] = 0.0 + +#dyn.body.FORCE_INIT[0] = 0.0 +#dyn.body.FORCE_INIT[1] = 0.0 +#dyn.body.FORCE_INIT[2] = 10.0 + +#dyn.body.TORQUE_INIT[0] = 0.0 +#dyn.body.TORQUE_INIT[1] = 0.0 +#dyn.body.TORQUE_INIT[2] = 0.0 + +#dyn.body.VEL_INIT[0] = 0.0 +#dyn.body.VEL_INIT[1] = 0.0 +#dyn.body.VEL_INIT[2] = 0.0 + +#dyn.body.POS_FORCE[0] = 2.0 +#dyn.body.POS_FORCE[1] = 0.0 +#dyn.body.POS_FORCE[2] = 0.0 + +################################################################################ +# SIMULATION SET UP # +# This is where we set up the remaining inputs to create a working sim. # +################################################################################ + +# ============================================================================ - +# getintegrator(integration method, number of variables to be integrated) +# This sets up the integration scheme we use to analyze this system. +# ============================================================================ - +dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 12) + +# ============================================================================ - +# This tells the sim to stop after a few seconds +# ============================================================================ - +trick.stop(10.0) diff --git a/trick_sims/SIM_singlerigidbody/S_define b/trick_sims/SIM_singlerigidbody/S_define new file mode 100644 index 000000000..89cc89b58 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/S_define @@ -0,0 +1,32 @@ +/************************TRICK HEADER************************* +PURPOSE: + ( Single body rigid dynamics simulation ) +LIBRARY DEPENDENCIES: + ((src/body.cpp) + (src/body_shutdown.cpp) + ) +*************************************************************/ +#define TRICK_NO_MONTE_CARLO +#define TRICK_NO_MASTERSLAVE +#define TRICK_NO_INSTRUMENTATION +#define TRICK_NO_REALTIMEINJECTOR +#define TRICK_NO_ZEROCONF + +#include "sim_objects/default_trick_sys.sm" +##include "include/body.hh" +class BodySimObject : public Trick::SimObject { + public: + BODY body; + + BodySimObject() { + ("default_data") body.default_data() ; + ("initialization") body.init() ; + ("derivative") body.derivative() ; + ("integration") trick_ret = body.integ() ; + ("shutdown") body.body_shutdown(); + } +}; + +BodySimObject dyn; +IntegLoop dyn_integloop(0.01) dyn; + diff --git a/trick_sims/SIM_singlerigidbody/S_overrides.mk b/trick_sims/SIM_singlerigidbody/S_overrides.mk new file mode 100644 index 000000000..abd95c9b1 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/S_overrides.mk @@ -0,0 +1,14 @@ +TRICK_CFLAGS += -Imodels +TRICK_CXXFLAGS += -Imodels + +.PHONY: clean_graphics + +all: models/singlerigidbodygraphics/build/SingleRigidBodyDisplay.jar + +spotless: clean_graphics + +models/singlerigidbodygraphics/build/SingleRigidBodyDisplay.jar: + ${MAKE} -C ./models/singlerigidbodygraphics + +clean_graphics: + ${MAKE} -C ./models/singlerigidbodygraphics clean diff --git a/trick_sims/SIM_singlerigidbody/models/include/body.hh b/trick_sims/SIM_singlerigidbody/models/include/body.hh new file mode 100644 index 000000000..2508bb49f --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/include/body.hh @@ -0,0 +1,78 @@ +/********************************************************************* +PURPOSE: (Simulate a single rigid body with six degrees of freedom) +LIBRARY DEPENDENCIES: + ((SingleRigidBody/src/body.o)) +*********************************************************************/ +#ifndef BODY_HH +#define BODY_HH + +class BODY { +public: + BODY(); + ~BODY(); + //Variables + + //Initializes state variables + double POS_INIT[3]; + double VEL_INIT[3]; + double ANGLE_INIT[3]; + double OMEGA_INIT[3]; + + //State variables + double pos[3]; + double vel[3]; + double angle[3]; + double omega[3]; + + double acc[3]; + double omegaDot[3]; + + double force[3]; + double FORCE_INIT[3]; + double force_remain[3]; + double POS_FORCE[3]; + + double torque[3]; + double TORQUE_INIT[3]; + double torque_remain[3]; + + double inertia; + double inertia_matrix[3][3]; + + double radius; + double mass; + double mat_mass[6][6]; + double massmatrix[3][3]; + + double CM[3]; + double CM_skew[3][3]; + double m_CM_skew[3][3]; + double neg_m_CM_skew[3][3]; + + double vec_a[6]; + double vec_b[6]; + + double rotate[3][3]; + double angle_force[3]; + + double tmp_vec[6]; + double **mat_mass_dyn; + double **mat_L; + + // Methods + void default_data(); + void init(); + void derivative(); + int integ(); + int body_shutdown(); + + void rotation_matrix(); + void calcforce(); + void calctorque(); + void calcforce_remain(); + void calctorque_remain(); + void eq_setup(); + void eq_solver(); +}; + +#endif diff --git a/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/Makefile b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/Makefile new file mode 100644 index 000000000..d3e0cbc07 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/Makefile @@ -0,0 +1,6 @@ + +all: + mvn package + +clean: + rm -rf build diff --git a/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/pom.xml b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/pom.xml new file mode 100644 index 000000000..9aa0f4421 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + trick-java + trick-java + 23.0.0-beta + + trick-java + + https://github.com/nasa/trick + + + UTF-8 + 1.8 + 1.8 + + + + + junit + junit + 4.13.1 + test + + + + + + SingleRigidBodyDisplay + + build + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + ${java.home}/bin/javadoc + ../../share/doc/trick/java + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + + maven-resources-plugin + 3.0.2 + + + + maven-compiler-plugin + 3.8.0 + + + -g + -Xlint:unchecked + -Xlint:deprecation + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + true + lib/ + trick.SRBDisplay + + + + + + + maven-surefire-plugin + 2.22.1 + + + + maven-install-plugin + 2.5.2 + + + + maven-deploy-plugin + 2.8.2 + + + + + maven-site-plugin + 3.7.1 + + + + + + + + \ No newline at end of file diff --git a/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/src/main/java/trick/matrixops/MatrixOps.java b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/src/main/java/trick/matrixops/MatrixOps.java new file mode 100644 index 000000000..463b303be --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/singlerigidbodygraphics/src/main/java/trick/matrixops/MatrixOps.java @@ -0,0 +1,152 @@ +// This file contains helper functions for the matrix operations necessary for the graphics to be generated. + +package trick.matrixOps; + + + +public class MatrixOps { // Defines the matrix operation class. + + public static void printMatrix(double M[][]) { // Prints out the matrix. + int M_rows = M.length; + int M_cols = M[0].length; + for (int i = 0; i < M_rows; i++) { + for (int j = 0; j < M_cols; j++) + System.out.print(" " + M[i][j]); + System.out.println(); + } + } + + public static void printDVector(double V[]) { // Prints out a vector passed as a double. + System.out.print("("); + for (int i = 0; i < V.length; i++) { + System.out.print(" " + V[i]); + } + System.out.println(")"); + } + + public static void printIVector(int V[]) { // Prints out a vector passed as an integer. + System.out.print("("); + for (int i = 0; i < V.length; i++) { + System.out.print(" " + V[i]); + } + System.out.println(")"); + } + + public static void MtimesM( double R[][], double A[][], double B[][]) { // Multiplies two matrices together. + int A_rows = A.length; + int A_cols = A[0].length; + int B_rows = B.length; + int B_cols = B[0].length; + int R_rows = R.length; + int R_cols = R[0].length; + + if (A_cols != B_rows) { // Checks if the matrices can be multiplied. + System.out.println( "\nNot possible to multiply matrixes,"); + System.out.println("where the first has " + A_cols + " columns,"); + System.out.println("and the second has " + B_rows + "rows."); + return; + } + if ((R_rows != A_rows) || (R_cols != B_cols)) { // Checks if the defined result matrix is the wrong size. + System.out.println( "\n Result matrix is wrong size."); + return; + } + + for (int i = 0; i < A_rows; i++) { // Multiplies the two matrices together. + for (int j = 0; j < B_cols; j++) { + R[i][j] = 0.0; + for (int k = 0; k < B_rows; k++) + R[i][j] += A[i][k] * B[k][j]; + } + } + } + + public static void MtimesV( double R[], double M[][], double V[]) { // Multiplies a matrix with a vector. + int M_rows = M.length; + int M_cols = M[0].length; + int V_rows = V.length; + + if (M_cols != V_rows) { // Checks if the matrix and the vector can be multiplied together. + System.out.println( "\nNot possible to multiply matrix and vector,"); + System.out.println( "where the matrix has " + M_cols + " columns,"); + System.out.println("and the vector has " + V_rows + " elements."); + return; + } + + if (R.length != M.length) { // Checks if the defined result vector is the wrong size. + System.out.println( "\n Result vector is wrong size."); + return; + } + + for (int i =0; i < M_rows ; i++) { // Multiplies the vector with the matrix. + R[i] = 0.0; + for (int j =0; j < M_cols ; j++) { + R[i] += M[i][j] * V[j]; + } + } + return; + } + + public static void VplusV(double R[], double A[], double B[]) { // Adds two matrices together. + if ((A.length != B.length) || (A.length != R.length)) { + System.out.println( "\n MatrixOps::VplusV : Vectors are not the same size."); + } + for (int i=0; i Math.PI) vantageAzimuth -= Math.PI; + if (vantageAzimuth < -Math.PI) vantageAzimuth += Math.PI; + vantageElevation -= (dy * Math.PI) / getHeight(); + if (vantageElevation > Math.toRadians( 89.0)) vantageElevation = Math.toRadians( 89.0); + if (vantageElevation < Math.toRadians(-89.0)) vantageElevation = Math.toRadians(-89.0); + setAzElRotation(worldToVantageRotation, vantageAzimuth, vantageElevation); + repaint(); + } + + public void setAzElRotation(double RotationMatrix[][], double azimuth, double elevation) { // Sets the azimuth and elevation rotation. + double Rotation_about_Y[][] = { + { Math.cos(elevation), 0.0, Math.sin(elevation)}, + { 0.0, 1.0, 0.0}, + {-Math.sin(elevation), 0.0, Math.cos(elevation)} + }; + + double Rotation_about_Z[][] = { + {Math.cos(azimuth), -Math.sin(azimuth), 0.0}, + {Math.sin(azimuth), Math.cos(azimuth), 0.0}, + { 0.0, 0.0, 1.0} + }; + MatrixOps.MtimesM( RotationMatrix, Rotation_about_Y, Rotation_about_Z); + } + + public void worldToScreenPoint( int result[], double V_world[]) { + double V_vantage[] = new double[3]; + // Tranform vector in world coordinates to vantage coordinates. + MatrixOps.MtimesV(V_vantage, worldToVantageRotation, V_world); + // Perspective projection of point in 3D vantage coordinates to 2D screen coordinates. + double perspective_scale = screen_half_width/(Math.tan(beta)*(vantageDistance-V_vantage[0])); + result[0] = (int)(perspective_scale * V_vantage[1] + screen_half_width); + result[1] = (int)(screen_half_height - perspective_scale * V_vantage[2]); + } + + public void setBodyPos( double x, double y, double z) { + bodyPos[0] = x; + bodyPos[1] = y; + bodyPos[2] = z; + } + + public void drawLineSegmentInWorld(Graphics2D g, Color color, double start[], double end[]) { + g.setPaint(color); + int start_screen[] = {0, 0}; + int end_screen[] = {0, 0}; + worldToScreenPoint( start_screen, start); + worldToScreenPoint( end_screen, end); + g.drawLine( start_screen[0], start_screen[1], end_screen[0], end_screen[1]); + } + + public void drawLabelInWorld(Graphics2D g, Color color, double loc_world[], String s) { + g.setPaint(color); + int loc_screen[] = {0, 0}; + worldToScreenPoint( loc_screen, loc_world); + g.drawString ( s, loc_screen[0], loc_screen[1]); + } + + private void doDrawing( Graphics g) { + Graphics2D g2d = (Graphics2D) g; + + int width = getWidth(); + int height = getHeight(); + + screen_half_width = (width/2); + screen_half_height = (height/2); + + g2d.setPaint(Color.WHITE); + g2d.fillRect(0, 0, width, height); + + + // Draw the vehicle. + + // Transform the vehicle vertices from body -> world, apply the vehicle position offset, and then to 2D screen points. + for (int i=0; i World -> Vantage + MatrixOps.MtimesV(veh_unit_normals_world[i], bodyToWorldRotation, veh_unit_normals_body[i]); + MatrixOps.MtimesV(veh_unit_normals_vantage[i], worldToVantageRotation, veh_unit_normals_world[i]); + + // Render the triangle only if it's facing us. + double facing_angle = MatrixOps.VdotV(veh_unit_normals_vantage[i], LOS_vantage); + if ( (facing_angle > 0.0) && (facing_angle < Math.toRadians(90))) { + + // Calculate the diffuse reflection intensity. + double neg_illumination_vector[] = {0.0, 0.0, 0.0}; + MatrixOps.Vscale(neg_illumination_vector, illumination_vector, -1.0); + double diffuse_intensity = MatrixOps.VdotV(neg_illumination_vector, veh_unit_normals_world[i]); + if (diffuse_intensity < 0.0) diffuse_intensity = 0.0; + + // Proportion of the total light due to ambient light. + // (1.0 - ambient) is the proportion of reflected light. + double ambient = 0.8; // Must be between 0.0 and 1.0. + + // The color intensity is a combination of ambient light intensity, + // and diffuse reflection intensity. + double color_intensity = (ambient + (1.0 - ambient) * diffuse_intensity); + + g2d.setPaint( new Color( (int)(bodyFillColor.getRed() * color_intensity), + (int)(bodyFillColor.getGreen() * color_intensity), + (int)(bodyFillColor.getBlue() * color_intensity))); + + // Draw the triangle. + int triangle_poly_x[] = {0, 0, 0}; + int triangle_poly_y[] = {0, 0, 0}; + // For each point of the triangle. + for (int j=0; j < 3; j++) { + + triangle_poly_x[j] = veh_vrtx_screen[ veh_triangles[i][j] ][0]; + triangle_poly_y[j] = veh_vrtx_screen[ veh_triangles[i][j] ][1]; + + } + g2d.fillPolygon(triangle_poly_x, triangle_poly_y, 3); + } + } + + // Draw Wireframe Model + + g2d.setPaint( bodyLineColor ); + + for (int i = 0; i\n" + + "----------------------------------------------------------------------\n" + ); + } + + public static void main(String[] args) throws IOException { + + String host = "localHost"; + int port = 0; + String bodyImageFile = null; + + int ii = 0; + while (ii < args.length) { + switch (args[ii]) { + case "-help" : + case "--help" : { + printHelpText(); + System.exit(0); + } break; + default : { + port = (Integer.parseInt(args[ii])); + } break; + } + ++ii; + } + + if (port == 0) { + System.out.println("No variable server port specified."); + printHelpText(); + System.exit(0); + } + + SingleRigidBodyView singleRigidBodyView = new SingleRigidBodyView(); + + SRBDisplay sd = new SRBDisplay(singleRigidBodyView); + sd.setVisible(true); + + double f_init_x = 0.0; + double f_init_y = 0.0; + double f_init_z = 0.0; + + double accX = 0.0; + double accY = 0.0; + double accZ = 0.0; + + double angleX = 0.0; + double angleY = 0.0; + double angleZ = 0.0; + + double f_angleX = 0.0; + double f_angleY = 0.0; + double f_angleZ = 0.0; + + double forceX = 0.0; + double forceY = 0.0; + double forceZ = 0.0; + + double omegaX = 0.0; + double omegaY = 0.0; + double omegaZ = 0.0; + + double omegaDotX = 0.0; + double omegaDotY = 0.0; + double omegaDotZ = 0.0; + + double posX = 0.0; + double posY = 0.0; + double posZ = 0.0; + + double Rxx = 0.0; + double Rxy = 0.0; + double Rxz = 0.0; + double Ryx = 0.0; + double Ryy = 0.0; + double Ryz = 0.0; + double Rzx = 0.0; + double Rzy = 0.0; + double Rzz = 0.0; + + double velX = 0.0; + double velY = 0.0; + double velZ = 0.0; + + System.out.println("Connecting to: " + host + ":" + port); + sd.connectToServer(host, port); + + sd.out.writeBytes("trick.var_set_client_tag(\"SRBDisplay\") \n" + + "trick.var_pause() \n" + + + "trick.var_add(\"dyn.body.FORCE_INIT[0]\") \n" + // 36 + "trick.var_add(\"dyn.body.FORCE_INIT[1]\") \n" + + "trick.var_add(\"dyn.body.FORCE_INIT[2]\") \n" + + + "trick.var_add(\"dyn.body.acc[0]\") \n" + + "trick.var_add(\"dyn.body.acc[1]\") \n" + + "trick.var_add(\"dyn.body.acc[2]\") \n" + + + "trick.var_add(\"dyn.body.angle[0]\") \n" + + "trick.var_add(\"dyn.body.angle[1]\") \n" + + "trick.var_add(\"dyn.body.angle[2]\") \n" + + + "trick.var_add(\"dyn.body.angle_force[0]\") \n" + + "trick.var_add(\"dyn.body.angle_force[1]\") \n" + + "trick.var_add(\"dyn.body.angle_force[2]\") \n" + + + "trick.var_add(\"dyn.body.force[0]\") \n" + + "trick.var_add(\"dyn.body.force[1]\") \n" + + "trick.var_add(\"dyn.body.force[2]\") \n" + + + "trick.var_add(\"dyn.body.omega[0]\") \n" + + "trick.var_add(\"dyn.body.omega[1]\") \n" + + "trick.var_add(\"dyn.body.omega[2]\") \n" + + + "trick.var_add(\"dyn.body.omegaDot[0]\") \n" + + "trick.var_add(\"dyn.body.omegaDot[1]\") \n" + + "trick.var_add(\"dyn.body.omegaDot[2]\") \n" + + + "trick.var_add(\"dyn.body.pos[0]\") \n" + + "trick.var_add(\"dyn.body.pos[1]\") \n" + + "trick.var_add(\"dyn.body.pos[2]\") \n" + + + "trick.var_add(\"dyn.body.rotate[0][0]\") \n" + + "trick.var_add(\"dyn.body.rotate[0][1]\") \n" + + "trick.var_add(\"dyn.body.rotate[0][2]\") \n" + + "trick.var_add(\"dyn.body.rotate[1][0]\") \n" + + "trick.var_add(\"dyn.body.rotate[1][1]\") \n" + + "trick.var_add(\"dyn.body.rotate[1][2]\") \n" + + "trick.var_add(\"dyn.body.rotate[2][0]\") \n" + + "trick.var_add(\"dyn.body.rotate[2][1]\") \n" + + "trick.var_add(\"dyn.body.rotate[2][2]\") \n" + + + "trick.var_add(\"dyn.body.vel[0]\") \n" + + "trick.var_add(\"dyn.body.vel[1]\") \n" + + "trick.var_add(\"dyn.body.vel[2]\") \n" + + + "trick.var_ascii() \n" + + "trick.var_cycle(0.1) \n" + + "trick.var_unpause()\n" ); + + sd.out.flush(); + + sd.drawSRBView(); + + Boolean go = true; + + while (go) { + + String field[]; + + try { + String line; + line = sd.in.readLine(); + field = line.split("\t"); + + f_init_x = Double.parseDouble( field[1] ); + f_init_y = Double.parseDouble( field[2] ); + f_init_z = Double.parseDouble( field[3] ); + + accX = Double.parseDouble( field[4] ); + accY = Double.parseDouble( field[5] ); + accZ = Double.parseDouble( field[6] ); + + angleX = Double.parseDouble( field[7] ); + angleY = Double.parseDouble( field[8] ); + angleZ = Double.parseDouble( field[9] ); + + f_angleX = Double.parseDouble( field[10] ); + f_angleY = Double.parseDouble( field[11] ); + f_angleZ = Double.parseDouble( field[12] ); + + forceX = Double.parseDouble( field[13] ); + forceY = Double.parseDouble( field[14] ); + forceZ = Double.parseDouble( field[15] ); + + omegaX = Double.parseDouble( field[16] ); + omegaY = Double.parseDouble( field[17] ); + omegaZ = Double.parseDouble( field[18] ); + + omegaDotX = Double.parseDouble( field[19] ); + omegaDotY = Double.parseDouble( field[20] ); + omegaDotZ = Double.parseDouble( field[21] ); + + posX = Double.parseDouble( field[22] ); + posY = Double.parseDouble( field[23] ); + posZ = Double.parseDouble( field[24] ); + + Rxx = Double.parseDouble( field[25] ); + Rxy = Double.parseDouble( field[26] ); + Rxz = Double.parseDouble( field[27] ); + Ryx = Double.parseDouble( field[28] ); + Ryy = Double.parseDouble( field[29] ); + Ryz = Double.parseDouble( field[30] ); + Rzx = Double.parseDouble( field[31] ); + Rzy = Double.parseDouble( field[32] ); + Rzz = Double.parseDouble( field[33] ); + + velX = Double.parseDouble( field[34] ); + velY = Double.parseDouble( field[35] ); + velZ = Double.parseDouble( field[36] ); + + // Set the body position + singleRigidBodyView.setBodyPos(posX, posY, posZ); + + singleRigidBodyView.setBodyToWorldRotation( Rxx, Rxy, Rxz, + Ryx, Ryy, Ryz, + Rzx, Rzy, Rzz ); + + } catch (IOException | NullPointerException e ) { + go = false; + } + sd.drawSRBView(); + } + } +} \ No newline at end of file diff --git a/trick_sims/SIM_singlerigidbody/models/src/body.cpp b/trick_sims/SIM_singlerigidbody/models/src/body.cpp new file mode 100644 index 000000000..7721ef449 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/src/body.cpp @@ -0,0 +1,314 @@ +/************************************************************************** +PURPOSE: (Test trick macros (matrix and vector) as well as trick functions + including euler_matrix, deuler_123, and dLU_Choleski. Perform a + single body motion with six degrees of freedom) +LIBRARY DEPENDENCIES: + ((src/body.o)) +**************************************************************************/ + +#include + +#include "trick/integrator_c_intf.h" +#include "trick/trick_math_proto.h" +#include "trick/trick_math_error.h" +#include "trick/reference_frame.h" +#include "trick/vector_macros.h" +#include "trick/matrix_macros.h" + +#include "../include/body.hh" + +BODY::BODY(){}; +BODY::~BODY(){}; + +/************************************************************************* +FUNCTION: SingleRigidBody::default_data() +PURPOSE:` (Creates an object) +*************************************************************************/ + +void BODY::default_data() { + + //Initializing vectors and matricies to zero. + + for(int i = 0; i<6; i++) + vec_a[i] = 0.0; + + + for(int i = 0; i<6; i++) + vec_b[i] = 0.0; + + + for(int i = 0; i<6; i++) + for(int j = 0; j<6; j++) + mat_mass[i][j] = 0.0; + V_INIT(pos); + V_INIT(vel); + V_INIT(acc); + V_INIT(force); + V_INIT(force_remain); + V_INIT(torque); + V_INIT(torque_remain); + V_INIT(angle); + V_INIT(omega); + V_INIT(omegaDot); + V_INIT(angle_force); + + M_INIT(CM_skew); + M_INIT(m_CM_skew); + M_INIT(massmatrix); + M_INIT(rotate); + + + // Inputs + mass = 20.0; + radius = 2.0; + + POS_INIT[0] = 0.0; + POS_INIT[1] = 0.0; + POS_INIT[2] = 0.0; + + ANGLE_INIT[0] = 0.0; + ANGLE_INIT[1] = 0.0; + ANGLE_INIT[2] = 0.0; + + OMEGA_INIT[0] = 0.0; + OMEGA_INIT[1] = 0.0; + OMEGA_INIT[2] = 0.0; + + FORCE_INIT[0] = 0.0; + FORCE_INIT[1] = 0.0; + FORCE_INIT[2] = 10.0; + + VEL_INIT[0] = 0.0; + VEL_INIT[1] = 0.0; + VEL_INIT[2] = 0.0; + + POS_FORCE[0] = 2.0; + POS_FORCE[1] = 0.0; + POS_FORCE[2] = 0.0; + +}; + +/************************************************************************ +FUNCTION: SingleRigidBody::init() +PURPOSE: (Initializing state variables, creating inertia and CoM skew + matrices) +************************************************************************/ + +void BODY::init() { + + V_COPY(pos, POS_INIT); + V_COPY(vel, VEL_INIT); + V_COPY(angle, ANGLE_INIT); + V_COPY(omega, OMEGA_INIT); + + // Center of mass skew matrix + V_INIT(CM); + + V_SKEW(CM_skew, CM); + + MxSCALAR(m_CM_skew, CM_skew, mass); + MxSCALAR(neg_m_CM_skew, m_CM_skew, -1.0); + + // Creates 3x3 matrix for inerta + inertia = (2.0/5.0) * mass * radius * radius; + inertia_matrix[0][0] = inertia_matrix[1][1] = inertia_matrix[2][2] = inertia; + + // Creates 3x3 matrix for mass + massmatrix[0][0] = massmatrix[1][1] = massmatrix[2][2] = mass; + + // Combines inertia, center of mass, and mass matrix into a 6x6 matrix + for(int i = 0; i<3; i++) + for(int j = 0; j<3; j++) + mat_mass[i][j] = massmatrix[i][j]; + + for(int i = 0; i<3; i++) + for(int j = 3; j<6; j++) + mat_mass[i][j] = neg_m_CM_skew[i][j-3]; + + for(int i = 3; i<6; i++) + for(int j = 0; j<3; j++) + mat_mass[i][j] = m_CM_skew[i-3][j]; + + for(int i = 3; i<6; i++) + for(int j = 3; j<6; j++) + mat_mass[i][j] = inertia_matrix[i-3][j-3]; + + // Temporary vector and dynamic memory for Choleski + for(int i = 0; i<6; i++) + tmp_vec[i] = 0.0; + + mat_L = new double*[6]; + mat_mass_dyn = new double*[6]; + +}; + +/***************************************************************************** +FUNCTION: BODY::rotation_matrix() +PURPOSE: (Calculates transformation matrix from provided angles) +*****************************************************************************/ +void BODY::rotation_matrix() { + + // euler_matrix outputs the rotate matrix from angle + euler_matrix(angle, rotate, 0, Roll_Pitch_Yaw); + +}; + +/****************************************************************************** +FUNCTION: BODY::calcforce() +PURPOSE: (Calculates force wrt inertial frame) +******************************************************************************/ +void BODY::calcforce() { + + MxV(force, rotate, FORCE_INIT); + +}; + +/***************************************************************************** +FUNCTION: BODY::calctorque() +PURPOSE: (Calculates torque wrt inertial frame) +*****************************************************************************/ +void BODY::calctorque() { + + double torque_force[3]; + + V_CROSS(torque_force, POS_FORCE, FORCE_INIT); + MxV(torque, rotate, torque_force); + +}; + +/**************************************************************************** +FUNCTION: BODY::calcforce_remain() +PURPOSE: (Calculate remaining force wrt inertial frame) +****************************************************************************/ +void BODY::calcforce_remain() { + + double cross1[3]; + double cross2[3]; + + V_CROSS(cross1, omega, CM); + V_CROSS(cross2, omega, cross1); + V_SCALE(force_remain, cross2, mass); + +}; + + +/*************************************************************************** +FUNCTION: BODY::calctorque_remain() +PURPOSE: (Calculate remaining torque wrt inertial frame) +***************************************************************************/ +void BODY::calctorque_remain() { + + double I_w[3]; + + MxV(I_w, inertia_matrix, omega); + V_CROSS(torque_remain, omega, I_w); + + +}; + +/*************************************************************************** +FUNCTION: BODY::eq_setup() +PURPOSE: (Establishes b column vector for final eq M*a=b) +***************************************************************************/ +void BODY::eq_setup() { + + double remainder_force[3]; + double remainder_torque[3]; + + // Subtracts remaining from force & torque to get vec_b. + V_SUB(remainder_force, force, force_remain); + V_SUB(remainder_torque, torque, torque_remain); + + for(int i = 0; i<3; i++){ + vec_b[i] = remainder_force[i]; + } + + for(int i = 3; i<6; i++){ + vec_b[i] = remainder_torque[i-3]; + } + +}; + +/************************************************************************** +FUNCTION: BODY::eq_solver() +PURPOSE: (Performs choleski decomposition and computes linear and angular + accelerations) +**************************************************************************/ + +void BODY::eq_solver() { + + // Solving a = b * M^-1 + + for(int i = 0; i<6; i++) + { + mat_mass_dyn[i] = mat_mass[i]; + mat_L[i] = new double[6]; + } + + dLU_Choleski(mat_mass_dyn, mat_L, tmp_vec, 6, vec_b, vec_a, 0); + + // Acceleration output + for(int i = 0; i<3; i++) + acc[i] = vec_a[i]; + + // Angular accleration output + for(int i = 0; i<3; i++) + omegaDot[i] = vec_a[i + 3]; + +}; + +/*************************************************************************** +FUNCTION: BODY::derivative() +PURPOSE: (Calls all functions in desired order for calculations) +***************************************************************************/ +void BODY::derivative() { + + rotation_matrix(); + calcforce(); + calctorque(); + calcforce_remain(); + calctorque_remain(); + eq_setup(); + eq_solver(); + +}; + +/*************************************************************************** +FUNCTION: BODY::integrate() +PURPOSE: (Sets up trick integration) +***************************************************************************/ + +int BODY::integ() { + + int integration_step; + + load_state( + &pos[0], &pos[1], &pos[2], + &vel[0], &vel[1], &vel[2], + &angle[0], &angle[1], &angle[2], + &omega[0], &omega[1], &omega[2], + + NULL); + + + load_deriv( + &vel[0], &vel[1], &vel[2], + &acc[0], &acc[1], &acc[2], + &omega[0], &omega[1], &omega[2], + &omegaDot[0], &omegaDot[1], &omegaDot[2], + + NULL); + + integration_step = integrate(); + + unload_state( + &pos[0], &pos[1], &pos[2], + &vel[0], &vel[1], &vel[2], + &angle[0], &angle[1], &angle[2], + &omega[0], &omega[1], &omega[2], + + NULL); + + return(integration_step); + +}; diff --git a/trick_sims/SIM_singlerigidbody/models/src/body_shutdown.cpp b/trick_sims/SIM_singlerigidbody/models/src/body_shutdown.cpp new file mode 100644 index 000000000..bd16d7660 --- /dev/null +++ b/trick_sims/SIM_singlerigidbody/models/src/body_shutdown.cpp @@ -0,0 +1,14 @@ +/************************************************************************ +PURPOSE: (Shutdown the simulation) +*************************************************************************/ +#include "../include/body.hh" +#include "trick/exec_proto.h" + +int BODY::body_shutdown() { + + delete[] mat_mass_dyn; + delete[] mat_L; + + return 0; +}; + diff --git a/trick_source/codegen/Interface_Code_Gen/FieldDescription.cpp b/trick_source/codegen/Interface_Code_Gen/FieldDescription.cpp index 2fd8a0aaf..5a88d892f 100644 --- a/trick_source/codegen/Interface_Code_Gen/FieldDescription.cpp +++ b/trick_source/codegen/Interface_Code_Gen/FieldDescription.cpp @@ -163,13 +163,13 @@ void FieldDescription::parseComment(std::string comment) { comment = get_regex_field(comment , "(.*)@?trick_io[\\({]([^\\)}]+)[\\)}]" , 1) + get_regex_field(comment , "@?trick_io[\\({]([^\\)}]+)[\\)}](.*)" , 2) ; } - ret_str = get_regex_field(comment , "@?io[\\({]([^\\)}]+)[\\)}]" , 1) ; + ret_str = get_regex_field(comment , "(^|[^*])@?io[\\({]([^\\)}]+)[\\)}]" , 2) ; if ( ! ret_str.empty()) { io = io_map[ret_str] ; if(debug_level >= 4) std::cout << "go for io " << io << std::endl ; io_found = true ; - comment = get_regex_field(comment , "(.*)@?io[\\({]([^\\)}]+)[\\)}]" , 1) + - get_regex_field(comment , "@?io[\\({]([^\\)}]+)[\\)}](.*)" , 2) ; + comment = get_regex_field(comment , "(.*)(^|[^*])@?io[\\({]([^\\)}]+)[\\)}]" , 1) + + get_regex_field(comment , "(^|[^*])@?io[\\({]([^\\)}]+)[\\)}](.*)" , 3) ; } /* diff --git a/trick_source/codegen/Interface_Code_Gen/PrintAttributes.cpp b/trick_source/codegen/Interface_Code_Gen/PrintAttributes.cpp index 758a84cd9..5d443163a 100644 --- a/trick_source/codegen/Interface_Code_Gen/PrintAttributes.cpp +++ b/trick_source/codegen/Interface_Code_Gen/PrintAttributes.cpp @@ -395,8 +395,11 @@ std::set PrintAttributes::getEmptyFiles() { const clang::FileEntry * fe = (*fi).first ; #if (LIBCLANG_MAJOR < 4) // TODO delete when RHEL 7 no longer supported std::string header_file_name = fe->getName() ; -#else +#elif (LIBCLANG_MAJOR >= 4 && LIBCLANG_MAJOR < 18) std::string header_file_name = fe->getName().str() ; +#else + const clang::FileEntryRef fer = fi->first ; + std::string header_file_name = fer.getName().str(); #endif if ( visited_files.find(header_file_name) != visited_files.end() ) { diff --git a/trick_source/codegen/Interface_Code_Gen/Utilities.cpp b/trick_source/codegen/Interface_Code_Gen/Utilities.cpp index 39bec7d5a..11080f277 100644 --- a/trick_source/codegen/Interface_Code_Gen/Utilities.cpp +++ b/trick_source/codegen/Interface_Code_Gen/Utilities.cpp @@ -66,68 +66,69 @@ std::string trim(const std::string& str, const std::string& whitespace ) { } bool isInUserCode( clang::CompilerInstance & ci , clang::SourceLocation sl , HeaderSearchDirs & hsd ) { - clang::FileID fid = ci.getSourceManager().getFileID(sl) ; bool ret = false ; - if ( ! fid.isInvalid() ) { - const clang::FileEntry * fe = ci.getSourceManager().getFileEntryForID(fid) ; - if ( fe != NULL ) { -#if (LIBCLANG_MAJOR < 4) // TODO delete when RHEL 7 no longer supported - char * resolved_path = almostRealPath( fe->getName() ) ; -#else - char * resolved_path = almostRealPath( fe->getName().str() ) ; -#endif - if ( resolved_path != NULL ) { - if ( hsd.isPathInUserDir(resolved_path)) { - ret = true ; - } - free(resolved_path) ; - } + char* resolved_path = getResolvedPath(ci, sl); + + if ( resolved_path != NULL ) { + if ( hsd.isPathInUserDir(resolved_path)) { + ret = true ; } + free(resolved_path) ; } + return ret ; } bool isInUserOrTrickCode( clang::CompilerInstance & ci , clang::SourceLocation sl , HeaderSearchDirs & hsd ) { - clang::FileID fid = ci.getSourceManager().getFileID(sl) ; bool ret = false ; - if ( ! fid.isInvalid() ) { - const clang::FileEntry * fe = ci.getSourceManager().getFileEntryForID(fid) ; - if ( fe != NULL ) { -#if (LIBCLANG_MAJOR < 4) // TODO delete when RHEL 7 no longer supported - char * resolved_path = almostRealPath( fe->getName() ) ; -#else - char * resolved_path = almostRealPath( fe->getName().str() ) ; -#endif - if ( resolved_path != NULL ) { - if ( hsd.isPathInUserOrTrickDir(resolved_path)) { - ret = true ; - } - free(resolved_path) ; - } + char* resolved_path = getResolvedPath(ci, sl); + + if ( resolved_path != NULL ) { + if ( hsd.isPathInUserOrTrickDir(resolved_path)) { + ret = true ; } + free(resolved_path) ; } + return ret ; } std::string getFileName( clang::CompilerInstance & ci , clang::SourceLocation sl , HeaderSearchDirs & hsd ) { - clang::FileID fid = ci.getSourceManager().getFileID(sl) ; std::string file_name; - char* resolved_path; + char* resolved_path = getResolvedPath(ci, sl); + + if (resolved_path != NULL ) { + if (hsd.isPathInUserDir(resolved_path)) { + file_name.append(resolved_path); + } + free(resolved_path); + } + + return file_name; +} + +char * getResolvedPath(clang::CompilerInstance & ci , clang::SourceLocation sl) { + clang::FileID fid = ci.getSourceManager().getFileID(sl) ; + char* resolved_path = NULL; + if ( ! fid.isInvalid() ) { const clang::FileEntry * fe = ci.getSourceManager().getFileEntryForID(fid) ; if ( fe != NULL ) { #if (LIBCLANG_MAJOR < 4) // TODO delete when RHEL 7 no longer supported - char * resolved_path = almostRealPath( fe->getName() ) ; + resolved_path = almostRealPath( fe->getName() ) ; +#elif (LIBCLANG_MAJOR >= 4 && LIBCLANG_MAJOR < 18) + resolved_path = almostRealPath( fe->getName().str() ) ; #else - char * resolved_path = almostRealPath( fe->getName().str() ) ; + const clang::CustomizableOptional cfer = ci.getSourceManager().getFileEntryRefForID(fid) ; + if (cfer.has_value()) { + resolved_path = almostRealPath( cfer->getName().str() ) ; + } + #endif - if ( resolved_path != NULL and hsd.isPathInUserDir(resolved_path)) { - file_name.append(resolved_path); - } - free(resolved_path); } } - return file_name; + + return resolved_path; } #include diff --git a/trick_source/codegen/Interface_Code_Gen/Utilities.hh b/trick_source/codegen/Interface_Code_Gen/Utilities.hh index 6fcfe13d4..90491c0c9 100644 --- a/trick_source/codegen/Interface_Code_Gen/Utilities.hh +++ b/trick_source/codegen/Interface_Code_Gen/Utilities.hh @@ -21,7 +21,7 @@ bool isInUserOrTrickCode( clang::CompilerInstance & ci , clang::SourceLocation s std::string getFileName( clang::CompilerInstance & ci , clang::SourceLocation sl , HeaderSearchDirs & hsd ) ; char * almostRealPath( const std::string& in_path ) ; char * almostRealPath( const char * in_path ) ; - +char * getResolvedPath(clang::CompilerInstance & ci , clang::SourceLocation sl); std::string color(const Color& color, const std::string& text); std::string bold(const std::string& text); std::string underline(const std::string& text); diff --git a/trick_source/codegen/Interface_Code_Gen/main.cpp b/trick_source/codegen/Interface_Code_Gen/main.cpp index 843803291..41d26c4a9 100644 --- a/trick_source/codegen/Interface_Code_Gen/main.cpp +++ b/trick_source/codegen/Interface_Code_Gen/main.cpp @@ -312,7 +312,7 @@ int main(int argc, char * argv[]) { #if (LIBCLANG_MAJOR >= 10 && LIBCLANG_MAJOR < 18) const clang::FileEntry* fileEntry = ci.getFileManager().getFile(inputFilePath).get(); #elif (LIBCLANG_MAJOR >= 18) - clang::FileEntryRef fileEntryRef = llvm::cantFail(ci.getFileManager().getFileRef(inputFilePath)); + const clang::FileEntryRef fileEntryRef = llvm::cantFail(ci.getFileManager().getFileRef(inputFilePath)); #else const clang::FileEntry* fileEntry = ci.getFileManager().getFile(inputFilePath); #endif diff --git a/trick_source/data_products/DPX/APPS/FXPLOT/makefile b/trick_source/data_products/DPX/APPS/FXPLOT/makefile index b3bb90b1f..480cc1ece 100644 --- a/trick_source/data_products/DPX/APPS/FXPLOT/makefile +++ b/trick_source/data_products/DPX/APPS/FXPLOT/makefile @@ -2,7 +2,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -CC = cc +CC = gcc CPP = c++ DPX_DIR = ../.. diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/makefile b/trick_source/data_products/DPX/APPS/GXPLOT/makefile index e23337c54..e36352be0 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/makefile +++ b/trick_source/data_products/DPX/APPS/GXPLOT/makefile @@ -2,7 +2,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -CC = cc +CC = gcc CPP = c++ DPX_DIR = ../.. diff --git a/trick_source/data_products/DPX/DPC/makefile b/trick_source/data_products/DPX/DPC/makefile index 24e34a72a..14e3984a7 100644 --- a/trick_source/data_products/DPX/DPC/makefile +++ b/trick_source/data_products/DPX/DPC/makefile @@ -2,7 +2,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -CC = cc +CC = gcc CPP = c++ DPX_DIR = .. diff --git a/trick_source/data_products/DPX/DPM/makefile b/trick_source/data_products/DPX/DPM/makefile index 2b67ccd1b..a6fb2c76d 100644 --- a/trick_source/data_products/DPX/DPM/makefile +++ b/trick_source/data_products/DPX/DPM/makefile @@ -2,7 +2,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -CC = cc +CC = gcc CPP = c++ DPX_DIR = .. diff --git a/trick_source/data_products/EQParse/makefile b/trick_source/data_products/EQParse/makefile index 6a5cc3bd3..3887d161e 100644 --- a/trick_source/data_products/EQParse/makefile +++ b/trick_source/data_products/EQParse/makefile @@ -1,7 +1,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common -CC = cc +CC = gcc OBJ_DIR = object_${TRICK_HOST_CPU} LIBDIR = ../lib_${TRICK_HOST_CPU} diff --git a/trick_source/data_products/units/makefile b/trick_source/data_products/units/makefile index 20f28d6ca..42d7ac902 100644 --- a/trick_source/data_products/units/makefile +++ b/trick_source/data_products/units/makefile @@ -1,7 +1,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common -CC = cc +CC = gcc CPP = c++ ifndef TRICK_HOST_CPU diff --git a/trick_source/java/src/main/java/trick/common/ui/panels/FindBar.java b/trick_source/java/src/main/java/trick/common/ui/panels/FindBar.java index 2ebab2d1d..28b68be98 100644 --- a/trick_source/java/src/main/java/trick/common/ui/panels/FindBar.java +++ b/trick_source/java/src/main/java/trick/common/ui/panels/FindBar.java @@ -1,4 +1,3 @@ - //======================================== // Package //======================================== @@ -8,190 +7,282 @@ //Imports //======================================== -import java.awt.Color; -import java.awt.Component; -import java.awt.FlowLayout; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.*; + +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.SwingConstants; - -import org.jdesktop.swingx.AbstractPatternPanel; -import org.jdesktop.swingx.JXFindBar; -import org.jdesktop.swingx.JXFindPanel; -import org.jdesktop.swingx.search.Searchable; - +import java.util.ArrayList; +import java.util.List; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import org.jdesktop.swingx.JXEditorPane; /** - * A {@link JXFindBar} that is for allowing users to input search text. - * * @since Trick 10 + * A {@link JXFindBar} that allows users to input search text. + * This class extends JXFindBar, a SwingX component providing search bar functionality for a JXEditorPane. + * + * Notes for not extending JXFindBar: + * JXFindBar is a SwingX component extending JXFindPanel. + * JXFindPanel offers various search options, allowing users to input search text. + * When JXFindPanel performs a search, it calls the search method in the Searchable object. + * The Searchable search method is invoked when findNext or findPrevious is clicked. + * The JXEditorPane's Matcher calls toMatchResult() where the search text is used to find the match. + * The toMatchResult method is called in the Matcher class and creates a new Matcher, copying the state over. + * The toMatchResult method returns a {@link MatchResult} object. + * This works until JDK 9. + * JDK 9 introduces a new, non-public {@link java.util.regex.Matcher$ImmutableMatchResult} class that is the result of toMatchResult(). + * Therefore, a Matcher can't be cloned and the state can't be copied over. The following exception would be seen: + * java.lang.ClassCastException: class java.util.regex.Matcher$ImmutableMatchResult cannot be cast to class java.util.regex.Matcher + * (java.util.regex.Matcher$ImmutableMatchResult and java.util.regex.Matcher are in module java.base of loader 'bootstrap') + * + * @since Trick 19 + * This class no longer extends JXFindBar. It extends {@link JPanel} instead. + * FindBar is a custom JPanel that provides search functionality for a JXEditorPane. + * It allows users to search for text within the editor pane and navigate through the matches. + * + *

Usage example:

+ *
+ * {@code
+ * JXEditorPane editorPane = new JXEditorPane();
+ * FindBar findBar = new FindBar(editorPane);
+ * }
+ * 
*/ -public class FindBar extends JXFindBar implements ActionListener{ - - //======================================== - // Public data - //======================================== - - - //======================================== - // Protected data - //======================================== - - - //======================================== - // Private Data - //======================================= - - private static final long serialVersionUID = 9092192049485321408L; - - /** The initial search text for this find bar */ - private String initialSearchText = null; - - /** By default, this would not have search options as JXFindPanel, true otherwise. */ - private boolean hasOptions = false; - - // TODO: add pattern support - //private JCheckBox anchorCheck; - - //======================================== - // Constructors - //======================================== - /** - * Default constructor. - */ - public FindBar() { - super(); - setNotFoundForegroundColor(); - } - - /** - * The constructor that specifies total number of popup menus for the panel. - * - * @param searchable An instance of {@link Searchable} from a gui component which - * this search bar is for. - */ - public FindBar(Searchable searchable) { - super(searchable); - setNotFoundForegroundColor(); - } - - //======================================== - // Set/Get methods - //======================================== - /** - * Gets to see if case sensitive is checked. - * @return true or false - */ - public boolean isCaseSensitive() { - return getPatternModel().isCaseSensitive(); - } - /** - * Sets whether to have the options as {@link JXFindPanel}. - * @param b true or false - */ - public void setOptions(boolean b) { - hasOptions = b; - } - - /** - * Updates the searchField that is defined in parent class {@link AbstractPatternPanel} - * if there is initial search text is defined. - * @param searchText searchText - */ - public void updateSearchField(String searchText) { - initialSearchText = searchText; - if (searchField != null) { - searchField.setText(searchText); - } - } - - /** - * Gets the current search text shown in searchField. - * @return the text - */ - public String getSearchText() { - if (searchField != null) { - return searchField.getText(); - } - return null; - } - - /** - * Helper method for changing the forground color when the text is not found. - * Since notFoundForegroundColor is proteced in {@link JXFindBar}, extending - * it is the only way to be able to change it. - * - */ - private void setNotFoundForegroundColor() { - notFoundForegroundColor = Color.red; - } - - - //======================================== - // Methods - //======================================== - @Override - protected void build() { - if (hasOptions) { - buildBarWithOption(); - } else { - buildBar(); - } - if (initialSearchText != null) { - updateSearchField(initialSearchText); - } - } - - private void buildBar() { - setLayout(new FlowLayout(SwingConstants.LEADING)); - add(searchLabel); - add(new JLabel(":")); - add(new JLabel(" ")); +public class FindBar extends JPanel { + + // ======================================== + // Public data + // ======================================== + + // ======================================== + // Protected data + // ======================================== + + // ======================================== + // Private Data + // ======================================= + + /** + * A text field for entering search queries. + */ + private JTextField searchField; + /** + * A button that, when pressed, triggers the action to find the next occurrence + * of the search term in the text or document. + */ + private JButton findNextButton; + /** + * JButton used to trigger the action of finding the previous occurrence + * in a search operation within the UI. + */ + private JButton findPreviousButton; + /** + * The JXEditorPane component used for displaying and editing text content. + */ + private JXEditorPane editorPane; + /** + * A list of integer positions. + * This list stores the positions of all find matches. + */ + private List matchPosList; + /** + * A list of integers representing the lengths of each find match. + */ + private List matchLenList; + /** + * The current index used for tracking the position within a list or array. + * This variable is typically used to keep track of the current item being + * processed or displayed. + */ + private int currentIndex; + + // ======================================== + // Constructors + // ======================================== + /** + * Constructs a new FindBar with the specified JXEditorPane. + * + *

+ * The FindBar consists of a JTextField for entering the search text, and two buttons + * for navigating to the next and previous matches. It also highlights the current match + * in the editor pane. + *

+ * + *

+ * Features: + *

+ *
    + *
  • Real-time search and highlight as the user types in the search field.
  • + *
  • Navigation buttons to find the next and previous matches.
  • + *
+ * + * @param editorPane The JXEditorPane in which the search will be performed. + */ + public FindBar(JXEditorPane editorPane) { + this.editorPane = editorPane; + setLayout(new FlowLayout(FlowLayout.LEFT)); + + searchField = new JTextField(20); + findNextButton = new JButton("Find Next"); + findPreviousButton = new JButton("Find Previous"); + + // Add a DocumentListener to the search field to trigger search and highlight on + // text change as real-time search. + // The search field background color is set to red if no matches are found + // otherwise it is set to white. + searchField.getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + searchAndHighlight(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + searchAndHighlight(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + searchAndHighlight(); + } + + private void searchAndHighlight() { + String searchText = searchField.getText(); + findMatches(searchText); + highlightCurrentMatch(); + if (matchPosList == null || matchPosList.isEmpty()) { + searchField.setBackground(Color.RED); + } else { + searchField.setBackground(Color.WHITE); + } + } + }); + + // Add an ActionListener to the search field to trigger search and highlight on Enter key press. + searchField.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String searchText = searchField.getText(); + findMatches(searchText); + highlightCurrentMatch(); + } + }); + + // Add ActionListeners to the find next button. + findNextButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (matchPosList != null && !matchPosList.isEmpty()) { + currentIndex = (currentIndex + 1) % matchPosList.size(); + highlightCurrentMatch(); + } + } + }); + + // Add ActionListeners to the find previous button. + findPreviousButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (matchPosList != null && !matchPosList.isEmpty()) { + currentIndex = (currentIndex - 1 + matchPosList.size()) % matchPosList.size(); + highlightCurrentMatch(); + } + } + }); + + add(new JLabel("Find:")); add(searchField); - add(findNext); - add(findPrevious); - } - - private void buildBarWithOption() { - //anchorCheck = new JCheckBox("Anchor"); - //anchorCheck.addActionListener(this); - - wrapCheck = new JCheckBox(); - backCheck = new JCheckBox(); - Box lBox = new Box(BoxLayout.LINE_AXIS); - //lBox.add(anchorCheck); - lBox.add(matchCheck); - lBox.add(wrapCheck); - lBox.add(backCheck); - lBox.setAlignmentY(Component.TOP_ALIGNMENT); - - Box mBox = new Box(BoxLayout.LINE_AXIS); - mBox.add(searchLabel); - mBox.add(new JLabel(": ")); - mBox.add(searchField); - mBox.add(findNext); - mBox.add(findPrevious); - mBox.setAlignmentY(Component.TOP_ALIGNMENT); - - setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); - - add(lBox); - add(mBox); - } - - public void actionPerformed(ActionEvent e) { - /*if (e.getSource() == anchorCheck) { - if (anchorCheck.isSelected()) { - getPatternModel().setRegexCreatorKey(PatternModel.REGEX_ANCHORED); - } else { - getPatternModel().setMatchRule(PatternModel.REGEX_MATCH_RULES); - } - }*/ - } -} + add(findNextButton); + add(findPreviousButton); + } + + // ======================================== + // Set/Get methods + // ======================================== + + // ======================================== + // Methods + // ======================================== + /** + * Finds and highlights all matches of the given search text in the editor pane. + * + * @param searchText The text to search for within the editor pane. + * @throws PatternSyntaxException If the regular expression's syntax is invalid. + * @throws BadLocationException If the document's text cannot be retrieved. + */ + private void findMatches(String searchText) { + try { + Highlighter highlighter = editorPane.getHighlighter(); + Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW); + highlighter.removeAllHighlights(); + + Document doc = editorPane.getDocument(); + String text = doc.getText(0, doc.getLength()); + matchPosList = new ArrayList<>(); + matchLenList = new ArrayList<>(); + Pattern pattern = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(text); + + // Find all matches of the search text in the document. + // Add the start to matchPosList and the length to matchLenList. + while (matcher.find()) { + matchPosList.add(matcher.start()); + matchLenList.add(matcher.end() - matcher.start()); + } + + // Reset the current index to 0 and highlight the first match if available. + currentIndex = 0; + if (!matchPosList.isEmpty()) { + // Highlight the current match. + int pos = matchPosList.get(currentIndex); + highlighter.addHighlight(pos, pos + matchLenList.get(currentIndex), painter); + // Set the caret position to the start of the current match. + editorPane.setCaretPosition(pos); + } + } catch (PatternSyntaxException e) { + JOptionPane.showMessageDialog(this, "Invalid regular expression: " + e.getDescription(), "Error", + JOptionPane.ERROR_MESSAGE); + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + + /** + * Highlights the current match in the editor pane. + * + * This method uses the Highlighter to highlight the text in the editor pane + * that matches the current search term. It removes any existing highlights + * before applying the new highlight. If there are positions available in the + * list, it highlights the text at the current index position and sets the + * caret position to the start of the highlighted text. + * + * @throws BadLocationException if the position is invalid in the document model + */ + private void highlightCurrentMatch() { + try { + Highlighter highlighter = editorPane.getHighlighter(); + Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW); + highlighter.removeAllHighlights(); + + if (matchPosList != null && !matchPosList.isEmpty()) { + int pos = matchPosList.get(currentIndex); + // Highlight the current match. + highlighter.addHighlight(pos, pos + searchField.getText().length(), painter); + // Set the caret position to the start of the current match. + editorPane.setCaretPosition(pos); + } + } catch (BadLocationException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/trick_source/java/src/main/java/trick/sie/SieApplication.java b/trick_source/java/src/main/java/trick/sie/SieApplication.java index 4d772792d..f0fd71fd4 100644 --- a/trick_source/java/src/main/java/trick/sie/SieApplication.java +++ b/trick_source/java/src/main/java/trick/sie/SieApplication.java @@ -444,7 +444,7 @@ private void setupForViewingEnumeratedTypes(JComponent comp, Collection roots = new ConcurrentLinkedQueue(rootTemplates); executorService = Executors.newFixedThreadPool(threads); diff --git a/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java b/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java index b6b0ae9c6..8a77fd743 100644 --- a/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java +++ b/trick_source/java/src/main/java/trick/simcontrol/SimControlApplication.java @@ -1207,7 +1207,9 @@ private JPanel createStatusMsgPanel() { Font font = new Font("Monospaced", Font.PLAIN, curr_font_size); statusMsgPane.setFont(font); - JPanel statusMsgPanel = UIUtils.createSearchableTitledPanel("Status Messages", statusMsgPane, new FindBar(statusMsgPane.getSearchable())); + // Create a customized search bar for the status message pane. + FindBar findBar = new FindBar(statusMsgPane); + JPanel statusMsgPanel = UIUtils.createSearchableTitledPanel("Status Messages", statusMsgPane, findBar); return statusMsgPanel; } diff --git a/trick_source/sim_services/DataRecord/DRAscii.cpp b/trick_source/sim_services/DataRecord/DRAscii.cpp index 1d6f80ceb..55ce144a2 100644 --- a/trick_source/sim_services/DataRecord/DRAscii.cpp +++ b/trick_source/sim_services/DataRecord/DRAscii.cpp @@ -18,7 +18,7 @@ #include "trick/message_type.h" #include "trick/bitfield_proto.h" -Trick::DRAscii::DRAscii( std::string in_name ) : Trick::DataRecordGroup( in_name ) { +Trick::DRAscii::DRAscii( std::string in_name, Trick::DR_Type dr_type ) : Trick::DataRecordGroup( in_name, dr_type ) { ascii_float_format = "%20.8g" ; ascii_double_format = "%20.16g" ; diff --git a/trick_source/sim_services/DataRecord/DRBinary.cpp b/trick_source/sim_services/DataRecord/DRBinary.cpp index eaa8f314a..2b9b2038a 100644 --- a/trick_source/sim_services/DataRecord/DRBinary.cpp +++ b/trick_source/sim_services/DataRecord/DRBinary.cpp @@ -22,7 +22,7 @@ Other classes inherit from DRBinary. In these cases, we don't want to register the memory as DRBinary, so register_group will be set to false. */ -Trick::DRBinary::DRBinary( std::string in_name , bool register_group ) : Trick::DataRecordGroup(in_name) { +Trick::DRBinary::DRBinary( std::string in_name, bool register_group, Trick::DR_Type dr_type ) : Trick::DataRecordGroup(in_name, dr_type) { if ( register_group ) { register_group_with_mm(this, "Trick::DRBinary") ; } @@ -190,4 +190,4 @@ int Trick::DRBinary::format_specific_shutdown() { close(fd) ; } return(0) ; -} \ No newline at end of file +} diff --git a/trick_source/sim_services/DataRecord/DRHDF5.cpp b/trick_source/sim_services/DataRecord/DRHDF5.cpp index 8f70f1966..0ea955f67 100644 --- a/trick_source/sim_services/DataRecord/DRHDF5.cpp +++ b/trick_source/sim_services/DataRecord/DRHDF5.cpp @@ -14,7 +14,7 @@ #include "trick/memorymanager_c_intf.h" #include "trick/message_proto.h" -Trick::DRHDF5::DRHDF5( std::string in_name ) : Trick::DataRecordGroup(in_name) { +Trick::DRHDF5::DRHDF5( std::string in_name, Trick::DR_Type dr_type ) : Trick::DataRecordGroup(in_name, dr_type) { register_group_with_mm(this, "Trick::DRHDF5") ; } diff --git a/trick_source/sim_services/DataRecord/DataRecordDispatcher.cpp b/trick_source/sim_services/DataRecord/DataRecordDispatcher.cpp index ac75689c0..9669ba705 100644 --- a/trick_source/sim_services/DataRecord/DataRecordDispatcher.cpp +++ b/trick_source/sim_services/DataRecord/DataRecordDispatcher.cpp @@ -180,6 +180,10 @@ void Trick::DataRecordDispatcher::remove_all_groups() { } } +/** + @details + -# Gets the data recording group by its name + */ Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(std::string in_name) { std::vector ::iterator it ; for ( it = groups.begin() ; it != groups.end() ; ++it ) { @@ -189,6 +193,25 @@ Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(std::string in_n return NULL ; } +/** + @details + -# Gets the data recording group by its id number + */ +Trick::DataRecordGroup * Trick::DataRecordDispatcher::get_group(int in_idx) { + if (!groups.empty() && in_idx > -1 && in_idx < groups.size()) { + return groups[in_idx]; + } + return NULL ; +} + +/** + @details + -# Gets the size of all added data recroding groups + */ +int Trick::DataRecordDispatcher::get_groups_size() { + return groups.size(); +} + /** @details -# If the writer thread condition variable is unlocked diff --git a/trick_source/sim_services/DataRecord/DataRecordGroup.cpp b/trick_source/sim_services/DataRecord/DataRecordGroup.cpp index 5808e9dcd..13d057fe7 100644 --- a/trick_source/sim_services/DataRecord/DataRecordGroup.cpp +++ b/trick_source/sim_services/DataRecord/DataRecordGroup.cpp @@ -54,7 +54,7 @@ Trick::DataRecordBuffer::~DataRecordBuffer() { free(ref) ; } -Trick::DataRecordGroup::DataRecordGroup( std::string in_name ) : +Trick::DataRecordGroup::DataRecordGroup( std::string in_name, Trick::DR_Type dr_type ) : record(true) , inited(false) , group_name(in_name) , @@ -96,18 +96,7 @@ Trick::DataRecordGroup::DataRecordGroup( std::string in_name ) : // sim object name name = std::string("trick_data_record_group_") + in_name ; - // add_jobs_to_queue will fill in job_id later - // make the init job run after all other initialization jobs but before the post init checkpoint - // job so users can allocate memory in initialization jobs and checkpointing data rec groups will work - add_job(0, 1, (char *)"initialization", NULL, cycle, (char *)"init", (char *)"TRK", 65534) ; - add_job(0, 2, (char *)"end_of_frame", NULL, 1.0, (char *)"write_data", (char *)"TRK") ; - add_job(0, 3, (char *)"checkpoint", NULL, 1.0, (char *)"checkpoint", (char *)"TRK") ; - add_job(0, 4, (char *)"post_checkpoint", NULL, 1.0, (char *)"clear_checkpoint_vars", (char *)"TRK") ; - // run the restart job in phase 60001 - add_job(0, 5, (char *)"restart", NULL, 1.0, (char *)"restart", (char *)"TRK", 60001) ; - add_job(0, 6, (char *)"shutdown", NULL, 1.0, (char *)"shutdown", (char *)"TRK") ; - - write_job = add_job(0, 99, (char *)job_class.c_str(), NULL, cycle, (char *)"data_record" , (char *)"TRK") ; + configure_jobs(dr_type) ; add_time_variable() ; } @@ -429,6 +418,27 @@ int Trick::DataRecordGroup::init() { } +void Trick::DataRecordGroup::configure_jobs(DR_Type type) { + switch(type) { + default: + // run the restart job in phase 60001 + add_job(0, 5, (char *)"restart", NULL, 1.0, (char *)"restart", (char *)"TRK", 60001) ; + + case DR_Type::DR_Type_FrameLogDataRecord: + // add_jobs_to_queue will fill in job_id later + // make the init job run after all other initialization jobs but before the post init checkpoint + // job so users can allocate memory in initialization jobs and checkpointing data rec groups will work + add_job(0, 1, (char *)"initialization", NULL, cycle, (char *)"init", (char *)"TRK", 65534) ; + add_job(0, 2, (char *)"end_of_frame", NULL, 1.0, (char *)"write_data", (char *)"TRK") ; + add_job(0, 3, (char *)"checkpoint", NULL, 1.0, (char *)"checkpoint", (char *)"TRK") ; + add_job(0, 4, (char *)"post_checkpoint", NULL, 1.0, (char *)"clear_checkpoint_vars", (char *)"TRK") ; + add_job(0, 6, (char *)"shutdown", NULL, 1.0, (char *)"shutdown", (char *)"TRK") ; + + write_job = add_job(0, 99, (char *)job_class.c_str(), NULL, cycle, (char *)"data_record" , (char *)"TRK") ; + break ; + } +} + int Trick::DataRecordGroup::checkpoint() { unsigned int jj ; diff --git a/trick_source/sim_services/DataRecord/data_record_utilities.cpp b/trick_source/sim_services/DataRecord/data_record_utilities.cpp index 479ac758d..1077861e9 100644 --- a/trick_source/sim_services/DataRecord/data_record_utilities.cpp +++ b/trick_source/sim_services/DataRecord/data_record_utilities.cpp @@ -79,6 +79,20 @@ extern "C" Trick::DataRecordGroup * get_data_record_group( std::string in_name ) return NULL ; } +extern "C" Trick::DataRecordGroup * get_data_record_group_by_idx( int in_idx ) { + if ( the_drd != NULL ) { + return the_drd->get_group(in_idx) ; + } + return NULL ; +} + +extern "C" int get_num_data_record_groups() { + if ( the_drd != NULL ) { + return the_drd->get_groups_size() ; + } + return 0 ; +} + extern "C" int set_max_size_record_group (const char * in_name, uint64_t bytes ) { if ( the_drd != NULL ) { return the_drd->set_group_max_file_size(in_name, bytes ) ; diff --git a/trick_source/sim_services/DataTypes/makefile b/trick_source/sim_services/DataTypes/makefile index b5050c78f..c18d9b8e3 100644 --- a/trick_source/sim_services/DataTypes/makefile +++ b/trick_source/sim_services/DataTypes/makefile @@ -1,6 +1,6 @@ RM = rm -rf -CC = cc +CC = gcc CPP = c++ diff --git a/trick_source/sim_services/DebugPause/DebugPause.cpp b/trick_source/sim_services/DebugPause/DebugPause.cpp index b95bcbd20..91d4751ee 100644 --- a/trick_source/sim_services/DebugPause/DebugPause.cpp +++ b/trick_source/sim_services/DebugPause/DebugPause.cpp @@ -68,7 +68,7 @@ int Trick::DebugPause::debug_pause_on() { debug_pause_flag = true ; - sem_name_stream << "itimersepmaphore_" << getpid() ; + sem_name_stream << "debugstepmaphore_" << getpid() ; sem_name = sem_name_stream.str() ; debug_sem = sem_open(sem_name.c_str(), O_CREAT, S_IRWXU , 0); diff --git a/trick_source/sim_services/ExternalApplications/ExternalApplication.cpp b/trick_source/sim_services/ExternalApplications/ExternalApplication.cpp index ac377799f..1e540e92d 100644 --- a/trick_source/sim_services/ExternalApplications/ExternalApplication.cpp +++ b/trick_source/sim_services/ExternalApplications/ExternalApplication.cpp @@ -19,25 +19,13 @@ Trick::ExternalApplication::ExternalApplication() : host_source = port_source = AUTO; cycle_period_set = minimum_cycle_period_set = disconnect_behavior_set = height_set = width_set = x_set = y_set = auto_reconnect_set = false; - - // c_intf uses char *, we manage the memory here in external application - command_c_str = (char*)trick_MM->declare_var("char", (command.size() + 1) ); - strcpy(command_c_str, command.c_str()); - allocations.push_back(command_c_str); } Trick::ExternalApplication::~ExternalApplication() { - for(std::vector::iterator it = allocations.begin(); it != allocations.end(); ++it) { - trick_MM->delete_var( (void*)*it ); - } - allocations.clear(); } void Trick::ExternalApplication::set_startup_command(std::string in_command) { command = in_command; - command_c_str = (char*)trick_MM->declare_var("char", (command.size() + 1) ); - strcpy(command_c_str, command.c_str()); - allocations.push_back((command_c_str)); } std::string Trick::ExternalApplication::get_startup_command() { @@ -45,7 +33,7 @@ std::string Trick::ExternalApplication::get_startup_command() { } const char * Trick::ExternalApplication::get_startup_command_c_str() { - return command_c_str; + return command.c_str(); } void Trick::ExternalApplication::add_arguments(std::string args) { diff --git a/trick_source/sim_services/FrameLog/FrameDataRecordGroup.cpp b/trick_source/sim_services/FrameLog/FrameDataRecordGroup.cpp index c0da71b28..f66cb6a66 100644 --- a/trick_source/sim_services/FrameLog/FrameDataRecordGroup.cpp +++ b/trick_source/sim_services/FrameLog/FrameDataRecordGroup.cpp @@ -9,7 +9,7 @@ -# All instances get the end_of_frame frame_log_clear job. */ Trick::FrameDataRecordGroup::FrameDataRecordGroup( int in_thread_id , std::string in_name ) - : Trick::DRBinary(in_name, false) , thread_id(in_thread_id ) { + : Trick::DRBinary(in_name, false, Trick::DR_Type::DR_Type_FrameLogDataRecord ), thread_id(in_thread_id ) { if ( thread_id > 0 ) { add_job(thread_id, 1000, (char *)"top_of_frame", NULL, 1.0, (char *)"start_timer", (char *)"TRK", 1) ; // Frame logging uses phase 65533 in FrameLog.ccp. Stop the timer just before that. diff --git a/trick_source/sim_services/MemoryManager/MemoryManager_delete_var.cpp b/trick_source/sim_services/MemoryManager/MemoryManager_delete_var.cpp index 4bde1c45c..008485bf7 100644 --- a/trick_source/sim_services/MemoryManager/MemoryManager_delete_var.cpp +++ b/trick_source/sim_services/MemoryManager/MemoryManager_delete_var.cpp @@ -37,19 +37,18 @@ int Trick::MemoryManager::delete_var(void* address ) { MemoryManager allocated it. */ if ( alloc_info->stcl == TRICK_LOCAL ) { - if ( alloc_info->alloc_type == TRICK_ALLOC_MALLOC ) { + // The destructor that we just called MAY have deleted addresses + // that are already planned for deletion, say during reset_memory. + // So, keep a record of what we've recently deleted so we don't + // to warn that we can't find it, when reset_memory also tries to + // delete that same address. Same for TRICK_ALLOC_NEW block + deleted_addr_list.push_back(address); + if ( alloc_info->alloc_type == TRICK_ALLOC_MALLOC ) { // This will call a destructor ONLY if alloc_info->type is TRICK_STRUCTURED. // Otherwise it does nothing. io_src_destruct_class( alloc_info ); - // The destructor that we just called MAY have deleted addresses - // that are already planned for deletion, say during reset_memory. - // So, keep a record of what we've recently deleted so we don't - // to warn that we can't find it, when reset_memory also tries to - // delete that same address. - deleted_addr_list.push_back(address); - free( address); } else if ( alloc_info->alloc_type == TRICK_ALLOC_NEW ) { io_src_delete_class( alloc_info ); diff --git a/trick_source/sim_services/ScheduledJobQueue/ScheduledJobQueue.cpp b/trick_source/sim_services/ScheduledJobQueue/ScheduledJobQueue.cpp index 85824cf89..45660248d 100644 --- a/trick_source/sim_services/ScheduledJobQueue/ScheduledJobQueue.cpp +++ b/trick_source/sim_services/ScheduledJobQueue/ScheduledJobQueue.cpp @@ -91,6 +91,11 @@ int Trick::ScheduledJobQueue::push( JobData * new_job ) { /* Increment the size of the queue */ list_size++ ; + + int new_job_index = ((unsigned long)insert_pt - (unsigned long)list) / sizeof(JobData**); + if(new_job_index < curr_index) { + curr_index++; + } return(0) ; diff --git a/trick_source/sim_services/ScheduledJobQueue/test/ScheduledJobQueue_test.cpp b/trick_source/sim_services/ScheduledJobQueue/test/ScheduledJobQueue_test.cpp index ba1c3cc51..a1086b1c8 100644 --- a/trick_source/sim_services/ScheduledJobQueue/test/ScheduledJobQueue_test.cpp +++ b/trick_source/sim_services/ScheduledJobQueue/test/ScheduledJobQueue_test.cpp @@ -70,6 +70,117 @@ TEST_F( ScheduledJobQueueTest , PushJobsbyJobOrder ) { EXPECT_TRUE( sjq.empty() ) ; } +TEST_F( ScheduledJobQueueTest , PushJobOntoSameIndex_CurrIndex0 ) { + + Trick::JobData * job_ptr ; + + EXPECT_EQ( sjq.size() , (unsigned int)0) ; + EXPECT_TRUE( sjq.empty() ) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_4") ; + job_ptr->sim_object_id = 4 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)1) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)0) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_3") ; + job_ptr->sim_object_id = 3 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)2) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)0) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_2") ; + job_ptr->sim_object_id = 2 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)3) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)0) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_1") ; + job_ptr->sim_object_id = 1 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)4) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)0) ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_1") ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_2") ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_3") ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_4") ; + + sjq.clear() ; + EXPECT_EQ( sjq.size() , (unsigned int)0) ; + EXPECT_TRUE( sjq.empty() ) ; +} + +TEST_F( ScheduledJobQueueTest , PushJobOntoSameIndex_CurrIndex1 ) { + + Trick::JobData * job_ptr ; + + EXPECT_EQ( sjq.size() , (unsigned int)0) ; + EXPECT_TRUE( sjq.empty() ) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_1") ; + job_ptr->sim_object_id = 1 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + sjq.get_next_job() ; + + EXPECT_EQ( sjq.size() , (unsigned int)1) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)1) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_4") ; + job_ptr->sim_object_id = 4 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)2) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)1) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_3") ; + job_ptr->sim_object_id = 3 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)3) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)1) ; + + job_ptr = new Trick::JobData(0, 2 , "class_100", NULL, 1.0 , "job_2") ; + job_ptr->sim_object_id = 2 ; + job_ptr->job_class = 100 ; + sjq.push(job_ptr) ; + + EXPECT_EQ( sjq.size() , (unsigned int)4) ; + EXPECT_EQ( sjq.get_curr_index() , (unsigned int)1) ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_2") ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_3") ; + + job_ptr = sjq.get_next_job() ; + EXPECT_STREQ( job_ptr->name.c_str() , "job_4") ; + + sjq.clear() ; + EXPECT_EQ( sjq.size() , (unsigned int)0) ; + EXPECT_TRUE( sjq.empty() ) ; +} + TEST_F( ScheduledJobQueueTest , PushJobsbySimObjectOrder ) { //req.add_requirement("512154259"); diff --git a/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp b/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp index 643df11a2..0dbc11390 100644 --- a/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp +++ b/trick_source/sim_services/VariableServer/VariableServerListenThread.cpp @@ -112,21 +112,24 @@ int Trick::VariableServerListenThread::init_listen_device() { // Called from init jobs int Trick::VariableServerListenThread::check_and_move_listen_device() { - int ret ; + int ret = 0; if (_user_requested_address) { /* The user has requested a different source address or port in the input file */ _listener->disconnect(); ret = _listener->initialize(_requested_source_address, _requested_port); - _requested_port = _listener->getPort(); - _requested_source_address = _listener->getHostname(); + if (ret != 0) { message_publish(MSG_ERROR, "ERROR: Could not establish variable server source_address %s: port %d. Aborting.\n", _requested_source_address.c_str(), _requested_port); - return -1 ; + + ret = -1; } + + _requested_port = _listener->getPort(); + _requested_source_address = _listener->getHostname(); } - return 0 ; + return ret ; } void * Trick::VariableServerListenThread::thread_body() { @@ -248,7 +251,10 @@ int Trick::VariableServerListenThread::restart() { message_publish(MSG_INFO, "restart variable server message port = %d\n", _listener->getPort()); } - initializeMulticast(); + // Don't initialize the multicast group if it's already initialized + if (!_multicast->isInitialized()) { + initializeMulticast(); + } return 0 ; } diff --git a/trick_source/trick_utils/SAIntegrator/makefile b/trick_source/trick_utils/SAIntegrator/makefile index cdd6eaeb9..98506b78b 100644 --- a/trick_source/trick_utils/SAIntegrator/makefile +++ b/trick_source/trick_utils/SAIntegrator/makefile @@ -1,6 +1,6 @@ RM = rm -rf -CC = cc +CC = gcc CPP = c++ CFLAGS = -g -Wall -std=c++11 ${TRICK_CXXFLAGS} diff --git a/trick_source/web/CivetServer/makefile b/trick_source/web/CivetServer/makefile index fce4065f4..11db671d9 100644 --- a/trick_source/web/CivetServer/makefile +++ b/trick_source/web/CivetServer/makefile @@ -1,7 +1,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common RM = rm -rf -CC = cc +CC = gcc CPP = c++ CURL = curl MV = mv diff --git a/trick_source/web/dashboard/package-lock.json b/trick_source/web/dashboard/package-lock.json index 31567b33d..a1bed67cb 100644 --- a/trick_source/web/dashboard/package-lock.json +++ b/trick_source/web/dashboard/package-lock.json @@ -6052,9 +6052,9 @@ } }, "cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==" }, "cookie-signature": { "version": "1.0.6", @@ -6101,9 +6101,9 @@ "integrity": "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA==" }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6906,6 +6906,11 @@ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7883,16 +7888,16 @@ } }, "express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -7906,7 +7911,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -7925,18 +7930,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -7945,128 +7938,20 @@ "ms": "2.0.0" } }, - "encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==" - }, - "finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - } - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" - }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" - }, - "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "requires": { - "side-channel": "^1.0.6" - } + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "requires": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - } - }, - "side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - } } } }, @@ -8218,6 +8103,35 @@ "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" }, + "finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, "find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -9335,9 +9249,9 @@ } }, "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "requires": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -11901,6 +11815,11 @@ "fs-monkey": "^1.0.4" } }, + "merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==" + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -12092,9 +12011,9 @@ } }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" }, "native-promise-only": { "version": "0.8.1", @@ -14225,6 +14144,61 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" }, + "qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "requires": { + "side-channel": "^1.0.6" + }, + "dependencies": { + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + } + } + } + }, "querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -15006,9 +14980,9 @@ } }, "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "requires": { "fsevents": "~2.3.2" } @@ -15206,6 +15180,53 @@ } } }, + "send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "serialize-javascript": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", @@ -15274,6 +15295,17 @@ } } }, + "serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "requires": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + } + }, "set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
Related ProjectsFrequently Asked QuestionsHow-To GuidesRelated ProjectsFrequently Asked QuestionsHow-To Guides