From 0ce8acd694038a8fb63da528e71ce7b239715919 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 22 Jan 2026 17:03:42 -0500 Subject: [PATCH 1/2] NASA Docket No. GSC-19,200-1, and identified as "cFS Draco" Batch update of latest cFS software release Reflects commit 87a4ea3b0a58b93643351cc8c258602041663ea3 from NASA internal development repo --- .github/actions/check-coverage/action.yml | 33 + .github/actions/setup-osal/action.yml | 35 + .github/scripts/cppcheck-output.xslt | 17 + .github/scripts/lcov-output.xslt | 105 + .github/workflows/format-check.yml | 14 + .github/workflows/static-analysis.yml | 20 + .github/workflows/validation-test.yml | 135 + CMakeLists.txt | 93 + cfecfs/eds2cfetbl/CMakeLists.txt | 11 +- cfecfs/eds2cfetbl/eds2cfetbl.c | 921 +++++-- .../scripts/add_cfe_tables_impl.cmake | 27 +- cfecfs/eds2cfetbl/scripts/eds2cfetbl_rules.mk | 90 +- .../eds2cfetbl/scripts/eds_tbltool_filedef.h | 2 +- cfecfs/eds2cfetbl/scripts/extract_filedef.awk | 51 +- cfecfs/eds2cfetbl/scripts/extract_object.awk | 54 - .../eds2cfetbl/scripts/extract_typename.awk | 22 + cfecfs/eds2cfetbl/scripts/extract_varname.m4 | 1 - cfecfs/eds2cfetbl/scripts/filedef_extract.c | 10 + .../scripts/generate_eds_table_rules.cmake | 4 + .../eds2cfetbl/scripts/separate_filedef.awk | 24 + .../scripts/table_rule_template.d.in | 1 + cfecfs/eds2cfetbl/scripts/table_wrapper.c.m4 | 7 + cfecfs/eds2cfetbl/scripts/tabletool_rule.mk | 8 +- cfecfs/edsmsg/CMakeLists.txt | 26 +- cfecfs/edsmsg/fsw/inc/edsmsg_dispatcher.h | 80 + .../inc/{cfe_msg_hdr_eds.h => edsmsg_hdr.h} | 56 +- cfecfs/edsmsg/fsw/src/cfe_msg_dispatcher.c | 159 -- ...cfe_msg_commonhdr.c => edsmsg_commonhdr.c} | 48 +- cfecfs/edsmsg/fsw/src/edsmsg_dispatcher.c | 351 +++ .../fsw/src/{cfe_msg_init.c => edsmsg_init.c} | 2 +- ...cfe_msg_integrity.c => edsmsg_integrity.c} | 0 .../src/{cfe_msg_msgid.c => edsmsg_msgid.c} | 14 +- ...e_msg_sechdr_cmd.c => edsmsg_sechdr_cmd.c} | 10 +- ...e_msg_sechdr_tlm.c => edsmsg_sechdr_tlm.c} | 12 +- cfecfs/edsmsg/fsw/ut-stubs/CMakeLists.txt | 34 + .../fsw/ut-stubs/edsmsg_dispatcher_stubs.c | 48 + cfecfs/edsmsg/mission_build.cmake | 2 +- cfecfs/missionlib/CMakeLists.txt | 45 +- cfecfs/missionlib/arch_build.cmake | 23 +- .../cfe_mission_eds_interface_parameters.h.in | 10 +- cfecfs/missionlib/cmake/dbobj_patternrules.mk | 30 + cfecfs/missionlib/cmake/edstool-buildenv.d.in | 57 - .../missionlib/cmake/edstool-execute-arch.mk | 60 +- .../cmake/edstool-execute-mission.mk | 33 - .../missionlib/cmake/export-cmake-env.cmake | 5 + .../eds/70-cfe_sb_parameter_map.lua | 42 +- .../eds/75-cfe_sb_dispatch_tables.lua | 68 +- .../eds/76-cfe_sb_interfacedb_global.lua | 262 -- .../eds/76-cfe_sb_topicdb_global.lua | 104 + cfecfs/missionlib/eds/78-cfe_sb_global.lua | 64 + .../eds/80-build_cfe_sb_interfacedb.lua | 25 +- cfecfs/missionlib/eds/86-write_dds_idl.lua | 544 ++++ cfecfs/missionlib/eds/90-write_ccdd_json.lua | 92 +- cfecfs/missionlib/eds/91-write_cosmos_txt.lua | 55 +- cfecfs/missionlib/eds/instance-rules.xml | 3 + cfecfs/missionlib/fsw/CMakeLists.txt | 46 +- .../missionlib/fsw/inc/cfe_missionlib_api.h | 210 +- .../fsw/inc/cfe_missionlib_runtime.h | 2 +- .../missionlib/fsw/src/cfe_missionlib_api.c | 535 +--- .../fsw/src/cfe_missionlib_database_types.h | 66 +- .../fsw/src/cfe_missionlib_internal.h | 46 + .../fsw/src/cfe_missionlib_lookup.c | 93 + .../fsw/src/cfe_missionlib_runtime_default.c | 20 +- cfecfs/missionlib/fsw/ut-stubs/CMakeLists.txt | 4 + .../ut-stubs/cfe_missionlib_api_handlers.c | 96 +- .../fsw/ut-stubs/cfe_missionlib_api_stubs.c | 232 +- .../ut-stubs/cfe_missionlib_runtime_stubs.c | 12 +- cfecfs/missionlib/idl/header.idl | 35 + .../lua/inc/cfe_missionlib_lua_softwarebus.h | 6 +- .../lua/src/cfe_missionlib_lua_softwarebus.c | 278 +- cfecfs/missionlib/mission_build.cmake | 116 +- cfecfs/missionlib/python/CMakeLists.txt | 1 + .../python/inc/cfe_missionlib_python.h | 17 - .../src/cfe_missionlib_python_database.c | 348 +-- .../src/cfe_missionlib_python_interface.c | 313 ++- .../src/cfe_missionlib_python_internal.h | 53 +- .../python/src/cfe_missionlib_python_setup.c | 18 - .../python/src/cfe_missionlib_python_topic.c | 203 +- .../missionlib/unit-test/mission_build.cmake | 24 + cfecfs/testexecutive/CMakeLists.txt | 2 +- .../modules/ci_to_lab_interface.c | 2 +- cfecfs/util/CMakeLists.txt | 2 +- cfecfs/util/cmdUtil.c | 62 +- cfecfs/util/tlm_decode.c | 22 +- doc/eds-dom-reference.md | 10 +- edslib/CMakeLists.txt | 9 +- edslib/cmake/base_patternrules.mk | 26 + edslib/cmake/dbobj_patternrules.mk | 19 +- edslib/cmake/edstool-buildenv.mk.in | 24 + edslib/eds/40-seds_write_headers.lua | 117 +- .../eds/45-seds_write_datatypedb_objects.lua | 567 +++- .../eds/50-seds_write_displaydb_objects.lua | 31 +- edslib/eds/52-seds_write_intfdb_objects.lua | 468 ++++ edslib/eds/55-write_edsdb_makefiles.lua | 44 +- edslib/eds/59-load_edsdb.lua | 74 + edslib/fsw/CMakeLists.txt | 27 +- edslib/fsw/inc/edslib_api_types.h | 56 +- edslib/fsw/inc/edslib_database_ops.h | 68 + edslib/fsw/inc/edslib_database_types.h | 224 +- edslib/fsw/inc/edslib_datatypedb.h | 248 +- edslib/fsw/inc/edslib_global.h | 170 ++ edslib/fsw/inc/edslib_id.h | 136 +- edslib/fsw/inc/edslib_intfdb.h | 287 ++ edslib/fsw/src/edslib_binding_objects.c | 91 +- edslib/fsw/src/edslib_database_ops.c | 132 + edslib/fsw/src/edslib_datatypedb_api.c | 727 ++++- .../fsw/src/edslib_datatypedb_constraints.c | 523 +++- .../fsw/src/edslib_datatypedb_errorcontrol.c | 42 +- edslib/fsw/src/edslib_datatypedb_iterator.c | 16 +- edslib/fsw/src/edslib_datatypedb_load_store.c | 22 +- edslib/fsw/src/edslib_datatypedb_lookup.c | 228 +- .../fsw/src/edslib_datatypedb_pack_unpack.c | 1679 ++++++----- edslib/fsw/src/edslib_displaydb_api.c | 229 +- edslib/fsw/src/edslib_displaydb_base64.c | 15 +- edslib/fsw/src/edslib_displaydb_iterator.c | 25 +- edslib/fsw/src/edslib_displaydb_locate.c | 24 +- edslib/fsw/src/edslib_displaydb_lookup.c | 18 +- edslib/fsw/src/edslib_displaydb_stringconv.c | 42 +- edslib/fsw/src/edslib_global_api.c | 149 + edslib/fsw/src/edslib_init.c | 12 +- edslib/fsw/src/edslib_internal.h | 187 +- edslib/fsw/src/edslib_intfdb_api.c | 712 +++++ edslib/fsw/src/edslib_intfdb_lookup.c | 601 ++++ edslib/fsw/src/edslib_msgid_api.c | 113 - edslib/fsw/unit-test/CMakeLists.txt | 96 + edslib/fsw/unit-test/edslib_datatypedb_test.c | 542 ++++ edslib/fsw/unit-test/edslib_displaydb_test.c | 323 +++ edslib/fsw/unit-test/edslib_global_test.c | 97 + edslib/fsw/unit-test/edslib_intfdb_test.c | 247 ++ edslib/fsw/unit-test/edslib_native_vectors.c | 343 +++ edslib/fsw/unit-test/edslib_pack_test.c | 858 ++++++ edslib/fsw/unit-test/edslib_packed_vectors.c | 221 ++ edslib/fsw/unit-test/edslib_test_common.c | 50 + edslib/fsw/unit-test/edslib_test_common.h | 29 + edslib/fsw/unit-test/edslib_test_vectors.h | 110 + edslib/fsw/unit-test/edslib_unpack_test.c | 794 ++++++ edslib/fsw/ut-stubs/CMakeLists.txt | 27 +- .../ut-stubs/edslib_binding_objects_stubs.c | 10 +- .../fsw/ut-stubs/edslib_database_ops_stubs.c | 74 + edslib/fsw/ut-stubs/edslib_datatypedb_stubs.c | 138 +- edslib/fsw/ut-stubs/edslib_displaydb_stubs.c | 40 + edslib/fsw/ut-stubs/edslib_global_stubs.c | 116 + edslib/fsw/ut-stubs/edslib_intfdb_stubs.c | 275 ++ edslib/json/src/edslib_json_objects.c | 1 + edslib/lua/CMakeLists.txt | 4 +- edslib/lua/inc/edslib_lua51_compatibility.h | 239 -- edslib/lua/src/edslib_lua_objects.c | 39 +- edslib/python/CMakeLists.txt | 3 + edslib/python/src/edslib_python_conversions.c | 93 +- edslib/python/src/edslib_python_database.c | 324 ++- .../python/src/edslib_python_databaseentry.c | 100 +- edslib/python/src/edslib_python_internal.h | 8 +- .../python/src/edslib_python_packedobject.c | 7 + edslib/unit-test/CMakeLists.txt | 56 - edslib/unit-test/eds/UTHDR.xml | 80 - edslib/unit-test/eds/ut-base-types.xml | 48 - edslib/unit-test/edslib_basic_test.c | 622 ----- edslib/unit-test/edslib_full_test.c | 648 ----- edslib/unit-test/edslib_test.c | 43 - tool/CMakeLists.txt | 3 +- tool/cmake/edstool-execute-all.mk | 59 + tool/cmake/edstool-execute-target.cmake | 115 + tool/cmake/edstool-namespace.mk.in | 12 + .../cmake/edstool-sources.mk.in | 4 +- tool/scripts/10-seds_resolve_refs.lua | 148 +- .../15-seds_create_implicit_scalars.lua | 1 + tool/scripts/20-seds_resolve_sizes.lua | 71 +- tool/scripts/25-seds_resolve_constraints.lua | 396 ++- tool/scripts/30-seds_resolve_components.lua | 5 +- tool/src/seds_global.h | 7 +- tool/src/seds_memreq.c | 139 +- tool/src/seds_memreq.h | 2 +- tool/src/seds_outputfile.c | 73 +- tool/src/seds_preprocess.c | 30 +- tool/src/seds_range.c | 2453 +++++++++++++++++ tool/src/seds_range.h | 101 + tool/src/seds_runtime.lua | 48 +- tool/src/seds_tool_main.c | 10 +- tool/src/seds_tree_methods.lua | 57 +- tool/src/seds_tree_node.c | 17 +- tool/src/seds_tree_node.h | 3 + tool/src/seds_xmlparser.c | 28 + validation/eds/ccsds_sois_sampletypes.xml | 720 +++++ validation/eds/ccsds_sois_seds.xml | 175 ++ .../eds/UT1.xml => validation/eds/utapp.xml | 95 +- validation/eds/uthdr.xml | 50 + 186 files changed, 20077 insertions(+), 6555 deletions(-) create mode 100644 .github/actions/check-coverage/action.yml create mode 100644 .github/actions/setup-osal/action.yml create mode 100644 .github/scripts/cppcheck-output.xslt create mode 100644 .github/scripts/lcov-output.xslt create mode 100644 .github/workflows/format-check.yml create mode 100644 .github/workflows/static-analysis.yml create mode 100644 .github/workflows/validation-test.yml create mode 100644 CMakeLists.txt delete mode 100644 cfecfs/eds2cfetbl/scripts/extract_object.awk create mode 100644 cfecfs/eds2cfetbl/scripts/extract_typename.awk delete mode 100644 cfecfs/eds2cfetbl/scripts/extract_varname.m4 create mode 100644 cfecfs/eds2cfetbl/scripts/filedef_extract.c create mode 100644 cfecfs/eds2cfetbl/scripts/separate_filedef.awk create mode 100644 cfecfs/eds2cfetbl/scripts/table_wrapper.c.m4 create mode 100644 cfecfs/edsmsg/fsw/inc/edsmsg_dispatcher.h rename cfecfs/edsmsg/fsw/inc/{cfe_msg_hdr_eds.h => edsmsg_hdr.h} (74%) delete mode 100644 cfecfs/edsmsg/fsw/src/cfe_msg_dispatcher.c rename cfecfs/edsmsg/fsw/src/{cfe_msg_commonhdr.c => edsmsg_commonhdr.c} (87%) create mode 100644 cfecfs/edsmsg/fsw/src/edsmsg_dispatcher.c rename cfecfs/edsmsg/fsw/src/{cfe_msg_init.c => edsmsg_init.c} (96%) rename cfecfs/edsmsg/fsw/src/{cfe_msg_integrity.c => edsmsg_integrity.c} (100%) rename cfecfs/edsmsg/fsw/src/{cfe_msg_msgid.c => edsmsg_msgid.c} (85%) rename cfecfs/edsmsg/fsw/src/{cfe_msg_sechdr_cmd.c => edsmsg_sechdr_cmd.c} (91%) rename cfecfs/edsmsg/fsw/src/{cfe_msg_sechdr_tlm.c => edsmsg_sechdr_tlm.c} (88%) create mode 100644 cfecfs/edsmsg/fsw/ut-stubs/CMakeLists.txt create mode 100644 cfecfs/edsmsg/fsw/ut-stubs/edsmsg_dispatcher_stubs.c create mode 100644 cfecfs/missionlib/cmake/dbobj_patternrules.mk delete mode 100644 cfecfs/missionlib/cmake/edstool-buildenv.d.in delete mode 100644 cfecfs/missionlib/cmake/edstool-execute-mission.mk create mode 100644 cfecfs/missionlib/cmake/export-cmake-env.cmake delete mode 100644 cfecfs/missionlib/eds/76-cfe_sb_interfacedb_global.lua create mode 100644 cfecfs/missionlib/eds/76-cfe_sb_topicdb_global.lua create mode 100644 cfecfs/missionlib/eds/78-cfe_sb_global.lua create mode 100644 cfecfs/missionlib/eds/86-write_dds_idl.lua create mode 100644 cfecfs/missionlib/fsw/src/cfe_missionlib_internal.h create mode 100644 cfecfs/missionlib/fsw/src/cfe_missionlib_lookup.c create mode 100644 cfecfs/missionlib/idl/header.idl create mode 100644 cfecfs/missionlib/unit-test/mission_build.cmake create mode 100644 edslib/cmake/base_patternrules.mk create mode 100644 edslib/cmake/edstool-buildenv.mk.in create mode 100644 edslib/eds/52-seds_write_intfdb_objects.lua create mode 100644 edslib/eds/59-load_edsdb.lua create mode 100644 edslib/fsw/inc/edslib_database_ops.h create mode 100644 edslib/fsw/inc/edslib_global.h create mode 100644 edslib/fsw/inc/edslib_intfdb.h create mode 100644 edslib/fsw/src/edslib_database_ops.c create mode 100644 edslib/fsw/src/edslib_global_api.c create mode 100644 edslib/fsw/src/edslib_intfdb_api.c create mode 100644 edslib/fsw/src/edslib_intfdb_lookup.c delete mode 100644 edslib/fsw/src/edslib_msgid_api.c create mode 100644 edslib/fsw/unit-test/CMakeLists.txt create mode 100644 edslib/fsw/unit-test/edslib_datatypedb_test.c create mode 100644 edslib/fsw/unit-test/edslib_displaydb_test.c create mode 100644 edslib/fsw/unit-test/edslib_global_test.c create mode 100644 edslib/fsw/unit-test/edslib_intfdb_test.c create mode 100644 edslib/fsw/unit-test/edslib_native_vectors.c create mode 100644 edslib/fsw/unit-test/edslib_pack_test.c create mode 100644 edslib/fsw/unit-test/edslib_packed_vectors.c create mode 100644 edslib/fsw/unit-test/edslib_test_common.c create mode 100644 edslib/fsw/unit-test/edslib_test_common.h create mode 100644 edslib/fsw/unit-test/edslib_test_vectors.h create mode 100644 edslib/fsw/unit-test/edslib_unpack_test.c create mode 100644 edslib/fsw/ut-stubs/edslib_database_ops_stubs.c create mode 100644 edslib/fsw/ut-stubs/edslib_global_stubs.c create mode 100644 edslib/fsw/ut-stubs/edslib_intfdb_stubs.c delete mode 100644 edslib/lua/inc/edslib_lua51_compatibility.h delete mode 100644 edslib/unit-test/CMakeLists.txt delete mode 100644 edslib/unit-test/eds/UTHDR.xml delete mode 100644 edslib/unit-test/eds/ut-base-types.xml delete mode 100644 edslib/unit-test/edslib_basic_test.c delete mode 100644 edslib/unit-test/edslib_full_test.c delete mode 100644 edslib/unit-test/edslib_test.c create mode 100644 tool/cmake/edstool-execute-all.mk create mode 100644 tool/cmake/edstool-execute-target.cmake create mode 100644 tool/cmake/edstool-namespace.mk.in rename cfecfs/missionlib/cmake/edstool-sources.d.in => tool/cmake/edstool-sources.mk.in (58%) create mode 100644 tool/src/seds_range.c create mode 100644 tool/src/seds_range.h create mode 100644 validation/eds/ccsds_sois_sampletypes.xml create mode 100644 validation/eds/ccsds_sois_seds.xml rename edslib/unit-test/eds/UT1.xml => validation/eds/utapp.xml (61%) create mode 100644 validation/eds/uthdr.xml diff --git a/.github/actions/check-coverage/action.yml b/.github/actions/check-coverage/action.yml new file mode 100644 index 0000000..3006b4f --- /dev/null +++ b/.github/actions/check-coverage/action.yml @@ -0,0 +1,33 @@ +name: Check Coverage Results +description: 'Extracts a summary of the code coverage results' + +inputs: + binary-dir: + description: 'Directory containing binary files' + required: true + source-dir: + description: 'Directory containing source code files' + default: ./source + +runs: + using: 'composite' + steps: + - name: Capture Results + shell: bash + run: lcov + --capture --rc lcov_branch_coverage=1 + --directory '${{ inputs.binary-dir }}' + --output-file '${{ inputs.binary-dir }}/coverage.info' + + - name: Generate HTML + shell: bash + run: genhtml + '${{ inputs.binary-dir }}/coverage.info' + --branch-coverage + --output-directory '${{ inputs.binary-dir }}/lcov-html' + + - name: Extract Summary + shell: bash + run: xsltproc --html + '${{ inputs.source-dir }}/.github/scripts/lcov-output.xslt' + '${{ inputs.binary-dir }}/lcov-html/index.html' > '${{ inputs.binary-dir }}/lcov-summary.xml' diff --git a/.github/actions/setup-osal/action.yml b/.github/actions/setup-osal/action.yml new file mode 100644 index 0000000..9981851 --- /dev/null +++ b/.github/actions/setup-osal/action.yml @@ -0,0 +1,35 @@ +name: Install OSAL +description: 'Builds and installs NASA OSAL into the workflow filesystem' + +inputs: + upstream-ref: + description: 'Upstream ref to check out' + default: main + upstream-repo: + description: 'Upstream repository to use' + default: nasa/osal + config-options: + description: 'Configuration options to pass to CMake' + default: -DCMAKE_BUILD_TYPE=Release -DOSAL_OMIT_DEPRECATED=TRUE -DENABLE_UNIT_TESTS=TRUE -DOSAL_CONFIG_DEBUG_PERMISSIVE_MODE=ON + staging-dir: + description: 'Directory to stage output' + required: true + +runs: + using: 'composite' + steps: + - name: Checkout OSAL + uses: actions/checkout@v4 + with: + repository: ${{ inputs.upstream-repo }} + ref: ${{ inputs.upstream-ref }} + path: osal-source + + - name: Set up OSAL build + shell: bash + run: cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DOSAL_SYSTEM_BSPTYPE=generic-linux ${{ inputs.config-options }} -S osal-source -B osal-build + + - name: Build and Install OSAL + shell: bash + working-directory: osal-build + run: make DESTDIR=${{ inputs.staging-dir }} install diff --git a/.github/scripts/cppcheck-output.xslt b/.github/scripts/cppcheck-output.xslt new file mode 100644 index 0000000..12c0b6a --- /dev/null +++ b/.github/scripts/cppcheck-output.xslt @@ -0,0 +1,17 @@ + + + +## CppCheck Summary + +| error | warning | style | performance | portability | information | +| --- | --- | --- | --- | --- | --- | +| | | | | | | + +| severity | location | error id | issue | +| --- | --- | --- | --- | +| | : | | | + + +** error(s) reported** + + diff --git a/.github/scripts/lcov-output.xslt b/.github/scripts/lcov-output.xslt new file mode 100644 index 0000000..77e2ea4 --- /dev/null +++ b/.github/scripts/lcov-output.xslt @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + X + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + +
+ + +
+ + + +

LCOV Report

+ + + + + + + + + + + + +
+ + +
diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml new file mode 100644 index 0000000..441199a --- /dev/null +++ b/.github/workflows/format-check.yml @@ -0,0 +1,14 @@ +name: Format Check + +# Run on all push and pull requests +on: + pull_request: + types: + - opened + - reopened + - synchronize + +jobs: + format-check: + name: Run format check + uses: nasa/cFS/.github/workflows/format-check.yml@main diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..40c4cd9 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,20 @@ +name: Static Analysis + +# Run on all push and pull requests +on: + push: + branches: + - dev + - main + pull_request: + types: + - opened + - reopened + - synchronize + workflow_dispatch: + +jobs: + + static-analysis: + name: Run cppcheck + uses: nasa/cFS/.github/workflows/static-analysis.yml@main diff --git a/.github/workflows/validation-test.yml b/.github/workflows/validation-test.yml new file mode 100644 index 0000000..0a481ba --- /dev/null +++ b/.github/workflows/validation-test.yml @@ -0,0 +1,135 @@ +name: Internal Build And Run EdsLib Validation Tests + +on: + push: + branches: + - dev + - main + pull_request: + types: + - opened + - reopened + - synchronize + workflow_dispatch: + +# Force bash to apply pipefail option so pipeline failures aren't masked +defaults: + run: + shell: bash + +env: + UPSTREAM_OSAL_REPO: nasa/osal + UPSTREAM_OSAL_REF: main + +jobs: + prepare-dependencies: + name: Prepare Dependencies + runs-on: ubuntu-latest + steps: + - name: Cache EdsLib Source + uses: actions/cache@v3 + id: cache-edslib + with: + path: ${{ github.workspace }}/source + key: edslib-source-${{ github.sha }} + + - name: Cache OSAL Dependency + uses: actions/cache@v3 + id: cache-osal + with: + path: ${{ github.workspace }}/osal-staging + key: edslib-dep-${{ env.UPSTREAM_OSAL_REPO }}@${{ env.UPSTREAM_OSAL_REF }} + + - name: Checkout EdsLib + if: steps.cache-edslib.outputs.cache-hit != 'True' + uses: actions/checkout@v4 + with: + path: source + + - name: Set up OSAL + if: steps.cache-osal.outputs.cache-hit != 'True' + uses: ./source/.github/actions/setup-osal + with: + staging-dir: ${GITHUB_WORKSPACE}/osal-staging + upstream-repo: ${{ env.UPSTREAM_OSAL_REPO }} + upstream-ref: ${{ env.UPSTREAM_OSAL_REF }} + + build-and-test: + name: Build EdsLib and Execute Tests + needs: [prepare-dependencies] + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + buildtype: [Debug, Release] + + env: + MATRIX_ID: matrix-${{ matrix.buildtype }} + + steps: + + # Note - caches were updated in "prepare-dependencies" job so all three of these should be a hit + - name: Retrieve Cached EdsLib Source + uses: actions/cache@v3 + id: restore-source + with: + path: ${{ github.workspace }}/source + key: edslib-source-${{ github.sha }} + + - name: Retrieve Cached OSAL Dependency + id: restore-osal + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/osal-staging + key: edslib-dep-${{ env.UPSTREAM_OSAL_REPO }}@${{ env.UPSTREAM_OSAL_REF }} + + # The files extracted from the cache only have owner permissions, + # so in order to make this work it needs to adjust this. + - name: Import OSAL + run: | + sudo cp -rv -t / ${GITHUB_WORKSPACE}/osal-staging/* + sudo find /usr/local -type d -exec chmod go+rx {} \; + sudo find /usr/local -type f -exec chmod go+r {} \; + sudo find /usr/local -type f -path '*/bin/*' -exec chmod go+x {} \; + + - name: Refresh Dynamic Linker Cache + run: sudo ldconfig + + - name: Set up EdsLib Build + run: cmake + -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} + -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE + -DENABLE_UNIT_TESTS=TRUE + -DCMAKE_PREFIX_PATH=/usr/local/lib/cmake + -S source -B ${{ env.MATRIX_ID }} + + - name: Build BPLib + working-directory: ${{ env.MATRIX_ID }} + run: make all + + - name: Check for Tests + run: if [ -e '${{ env.MATRIX_ID }}/CTestTestfile.cmake' ]; then echo 'RUN_TESTS=True' >> $GITHUB_ENV; fi + + - name: Run Tests + working-directory: ${{ env.MATRIX_ID }} + if: env.RUN_TESTS == 'True' + run: ctest --output-on-failure 2>&1 | tee ctest.log + + - name: Check Coverage + if: env.MATRIX_ID == 'matrix-Debug' + uses: ./source/.github/actions/check-coverage + with: + binary-dir: ${{ env.MATRIX_ID }} + + - name: Assemble Results + run: | + if [ -s '${{ env.MATRIX_ID }}/ctest.log' ]; then + echo '

CTest Execution

' >> $GITHUB_STEP_SUMMARY + echo '
' >> $GITHUB_STEP_SUMMARY
+            cat '${{ env.MATRIX_ID }}/ctest.log' >> $GITHUB_STEP_SUMMARY
+            echo '
' >> $GITHUB_STEP_SUMMARY + fi + if [ -s '${{ env.MATRIX_ID }}/lcov-summary.xml' ]; then + cat '${{ env.MATRIX_ID }}/lcov-summary.xml' >> $GITHUB_STEP_SUMMARY + fi diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d3c9225 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,93 @@ +# +# LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation +# +# Copyright (c) 2020 United States Government as represented by +# the Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +################################################################## +# +# Standalone EdsLib Build Recipe +# +# This builds the EDS library as a separate entity, not directly +# integrated into any other project. The artifacts of this build +# can then be installed into a directory and used in a separate +# project. +# +################################################################## + +cmake_minimum_required(VERSION 3.10) +project(EDS C) + +# The build options will be turned on +option(EDSLIB_BUILD_MODULE + "Build a general-purpose EdsLib module for dynamically-linked applications" + ON) + +option(EDSLIB_PYTHON_BUILD_STANDALONE_MODULE + "Build a generic Python module that can be imported into standalone Python code" + ON) + + +# If unit testing is enabled, this requires the UT assert library which +# is part of the NASA OSAL. This should be built separately and the path +# to this should be provided via the CMAKE_PREFIX_PATH configuration variable. +if (ENABLE_UNIT_TESTS) + enable_testing() + + # This currently does not support cross compiling because building the unit tests + # requires execution of the sedstool on the host. Running unit tests on a target + # is possible, but it requires SEPARATELY building the sedstool for the host and + # then using that in the cross build. This is not yet implemented. + if (CMAKE_CROSSCOMPILING) + message(FATAL_ERROR "Cannot build unit tests in a cross compile environment using this script") + endif() + + find_package(NasaOsal REQUIRED) + message(STATUS "Using external OSAL ${NasaOsal_VERSION} for unit testing") + +endif() + +add_compile_definitions(_XOPEN_SOURCE=600) + +# The EDS tool is in the same repo, so the relative path should be consistent +include (tool/cmake/edstool-execute-target.cmake) + +# Allow code to easily reference the generated include files +include_directories(${CMAKE_BINARY_DIR}/inc) + +add_subdirectory(edslib) +add_subdirectory(tool) + +if (ENABLE_UNIT_TESTS) + message(STATUS "Set up EDS validation test for STANDALONE build") + + edstool_find_eds_sources("ut" ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/edslib + ${CMAKE_CURRENT_SOURCE_DIR}/validation + ) + + edstool_setup_toplevel(edstool-execute-ut "ut" ${CMAKE_BINARY_DIR}) + + add_library(ut_eds_db STATIC IMPORTED GLOBAL) + set_target_properties(ut_eds_db PROPERTIES + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/ut_eds_db${CMAKE_SHARED_LIBRARY_SUFFIX}" + ) + + add_dependencies(ut_eds_db edstool-execute-ut) + +endif() + diff --git a/cfecfs/eds2cfetbl/CMakeLists.txt b/cfecfs/eds2cfetbl/CMakeLists.txt index 1319b74..fdaf457 100644 --- a/cfecfs/eds2cfetbl/CMakeLists.txt +++ b/cfecfs/eds2cfetbl/CMakeLists.txt @@ -34,7 +34,7 @@ target_link_libraries(eds2cfetbl edslib_lua cfe_missionlib_lua_softwarebus edslib_runtime_static - cfe_missionlib_interfacedb_static + cfe_missionlib_sb_dispatchdb_static cfe_edsdb_static dl ) @@ -43,12 +43,21 @@ set_target_properties(eds2cfetbl PROPERTIES ENABLE_EXPORTS TRUE) # Export relevant information so the parent script can invoke the tool set(CFS_TABLETOOL_SCRIPT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/scripts" CACHE INTERNAL "CFS table tool script directory") +set(TBLTOOL_EXTRA_FLAGS) +if (SIMULATION) + string(APPEND TBLTOOL_EXTRA_FLAGS " -e SIMULATION=${SIMULATION}") + if (${SIMULATION} STREQUAL "native") + string(APPEND TBLTOOL_EXTRA_FLAGS " -p") + endif() +endif() + add_custom_target(tabletool-execute COMMAND $(MAKE) CC="${CMAKE_C_COMPILER}" CFLAGS="${CMAKE_C_FLAGS}" AR="${CMAKE_AR}" TBLTOOL="$" + TBLTOOL_EXTRA_FLAGS=${TBLTOOL_EXTRA_FLAGS} cfetables WORKING_DIRECTORY "${MISSION_BINARY_DIR}/tables" diff --git a/cfecfs/eds2cfetbl/eds2cfetbl.c b/cfecfs/eds2cfetbl/eds2cfetbl.c index de40c45..9e53f4c 100644 --- a/cfecfs/eds2cfetbl/eds2cfetbl.c +++ b/cfecfs/eds2cfetbl/eds2cfetbl.c @@ -53,10 +53,13 @@ #include "scripts/eds_tbltool_filedef.h" #include "edslib_init.h" +#include "edslib_global.h" +#include "edslib_datatypedb.h" +#include "edslib_displaydb.h" +#include "edslib_intfdb.h" +#include "edslib_binding_objects.h" -/* compatibility shim to support compilation with Lua5.1 */ -#include "edslib_lua51_compatibility.h" - +#include "cfe_tbl_eds_defines.h" #include "cfe_tbl_filedef.h" #include "cfe_mission_eds_parameters.h" #include "cfe_mission_eds_interface_parameters.h" @@ -67,20 +70,59 @@ #include "cfe_missionlib_lua_softwarebus.h" #include "cfe_missionlib_runtime.h" +/* an identifying marker that will be put at the beginning of every "info" (non error/warning) line */ +#define EDSTABLETOOL_INFO "--> " + +/* an identifying marker that will be put at the beginning of every EDSTABLETOOL_WARNING "line */ +#define EDSTABLETOOL_WARNING " **WARNING** " + +/* an identifying marker that will be put at the beginning of every "error" line */ +#define EDSTABLETOOL_ERROR " **ERROR** " const char LUA_APPLIST_KEY = 0; +/* This structure captures the metadata from importing an object from C */ +typedef struct EdsTableTool_CObj +{ + /* this should capture the original CFE_TBL_FileDef object */ + CFE_TBL_FileDef_t FileDefInfo; + + /* all the remaining items are filled in as info is determined */ + size_t TotalObjectSize; + size_t ElementSize; + + void *ObjPtr; + + char CTypeName[64]; + char EdsTypeName[64]; + + EdsDataType_BASE_TYPES_ApiName_t RuntimeAppName; + EdsDataType_BASE_TYPES_ApiName_t TableName; + + EdsLib_Id_t IntfDataTypeEdsId; + EdsLib_Id_t CTypeEdsId; + EdsLib_Id_t SelectedTypeEdsId; + +} EdsTableTool_CObj_t; + + typedef struct EdsTableTool_Global { + /* The Instance number of the current target (in cases where it is instance specific) */ uint16_t ProcessorId; - uint16_t NumMultiple; - char EdsTypeName[64]; + /* Whether to allow "passthrough" encoding for objects that do not have EDS */ + bool AllowPassthrough; + + /* temporary holding buffer for binary objects imported from C table files */ + EdsTableTool_CObj_t CObj; } EdsTableTool_Global_t; EdsTableTool_Global_t EdsTableTool_Global; +/* The table load command is a fixed definition, does not need a lookup */ +const EdsLib_Id_t EDS_TABLE_LOAD_CMD_ID = EDSLIB_INTF_ID(EDS_INDEX(CFE_TBL), EdsCommand_CFE_TBL_Table_load_DECLARATION); /*---------------------------------------------------------------- * @@ -129,69 +171,67 @@ uint16_t EdsTableTool_GetProcessorId(void) * * Exports a C-defined object to the global instance * - * This expects a lua_State object as the abstract argument, - * and a table should be the topmost entry in the stack. The - * object(s) from C will be wrapped in a Lua object and appended - * to the table. + * This just saves the object info, including the data associated with it, + * into the global object so it can be dealt with later. It has to be copied + * to the global because when this is called the object exists only on the stack + * in the caller context. * *-----------------------------------------------------------------*/ -void EdsTableTool_DoExport(void *arg, const void *filedefptr, const void *objptr, size_t objsize) +void EdsTableTool_DoExport(const void *filedefptr, const void *objptr, size_t objsize, const char *typename, size_t typesize) { - lua_State *lua = arg; - const uint8_t *sptr = objptr; - void *tptr; - size_t tsz; - int tbl_pos; + EdsTableTool_CObj_t *CObj = &EdsTableTool_Global.CObj; - /* There should be a table at the top of the initial stack */ - tbl_pos = lua_gettop(lua); + memset(CObj, 0, sizeof(*CObj)); - lua_getglobal(lua, "EdsDB"); + /* Save the original file def info */ + memcpy(&CObj->FileDefInfo, filedefptr, sizeof(CFE_TBL_FileDef_t)); - while (objsize > 0) + /* + * The size specified in FILEDEF should concur with the size of the actual object. + * If the CFE_TBL_FILEDEF macro was used, then this should always be true because the macro + * uses sizeof() on the object just like the wrapper does. However some older definitions + * will create the CFE_TBL_FileDef_t object directly, and this could be different + */ + if (CObj->FileDefInfo.ObjectSize != objsize) { - /* Call "New_Object" for the object type */ - lua_getfield(lua, -1, "NewObject"); - lua_pushstring(lua, EdsTableTool_Global.EdsTypeName); - if (lua_pcall(lua, 1, 1, 1) != LUA_OK) - { - /* note - error handler already displayed error message */ - fprintf(stderr, "Failed to execute: EdsDB.NewObject(%s)\n", EdsTableTool_Global.EdsTypeName); - exit(EXIT_FAILURE); - } - - /* Fill the Lua object with the data from the C world */ - EdsLib_LuaBinding_GetNativeObject(lua, -1, &tptr, &tsz); - memcpy(tptr, sptr, tsz); - sptr += tsz; - objsize -= tsz; + fprintf(stderr, EDSTABLETOOL_WARNING "Size from FileDef (%lu) differs from actual object (%lu)\n", + (unsigned long)CObj->FileDefInfo.ObjectSize, (unsigned long)objsize); + } - /* Append to the table at the top of the initial stack */ - lua_rawseti(lua, tbl_pos, 1 + lua_rawlen(lua, tbl_pos)); + CObj->ObjPtr = malloc(objsize); + if (CObj->ObjPtr == NULL) + { + fprintf(stderr, EDSTABLETOOL_ERROR "malloc(): %s\n", strerror(errno)); + abort(); } - /* Reset the stack to the same point as it was initially */ - lua_settop(lua, tbl_pos); + memcpy(CObj->ObjPtr, objptr, objsize); + + strncpy(CObj->CTypeName, typename, sizeof(CObj->CTypeName)-1); + + CObj->TotalObjectSize = objsize; + CObj->ElementSize = typesize; } -void LoadTemplateFile(lua_State *lua, const char *Filename) +/*---------------------------------------------------------------- + * + * Preloads a "C" table definition for backward compatibilty + * The result of this is used to pre-fill the object buffer so scripts + * can still execute on this and change the output. If no script is + * executed, then this will be the final result. + * + * This will populate the "CObj" member of the global + * + *-----------------------------------------------------------------*/ +void EdsTableTool_LoadCTemplateFile(const char *Filename) { void *dlhandle; void *fptr; const char *tmpstr; - int obj_idx; - size_t RuntimeAppNameLen; - CFE_TBL_FileDef_t *CFE_TBL_FileDefPtr; - EdsDataType_BASE_TYPES_ApiName_t RuntimeAppName; - EdsDataType_BASE_TYPES_ApiName_t TableName; - const char *EdsAppName; - size_t EdsAppNameLen; - char EdsTypeName[64]; + void (*DynamicGeneratorFuncPtr)(void); char ModuleTempName[64]; - void (*DynamicGeneratorFuncPtr)(void *); - EdsLib_Id_t EdsId; - EdsLib_DataTypeDB_TypeInfo_t TypeInfo; - uint16_t AppIdx; + + printf(EDSTABLETOOL_INFO "Initializing table data from: %s\n", Filename); dlerror(); dlhandle = dlopen(Filename, RTLD_LAZY | RTLD_LOCAL); @@ -202,53 +242,119 @@ void LoadTemplateFile(lua_State *lua, const char *Filename) { tmpstr = "[Unknown Error]"; } - fprintf(stderr,"Load Error: %s\n", tmpstr); + fprintf(stderr, EDSTABLETOOL_ERROR "dlopen(): %s\n", tmpstr); exit(EXIT_FAILURE); } - CFE_TBL_FileDefPtr = (CFE_TBL_FileDef_t *)dlsym(dlhandle, "CFE_TBL_FileDef"); + /* The generated wrapper always has the same function name */ + fptr = dlsym(dlhandle, "EdsTableTool_GENERATOR"); tmpstr = dlerror(); - if (CFE_TBL_FileDefPtr == NULL || tmpstr != NULL) + if (fptr == NULL || tmpstr != NULL) { if (tmpstr == NULL) { - tmpstr = "[Table object is NULL]"; + tmpstr = "[Object pointer is NULL]"; } - fprintf(stderr,"Error looking up CFE_TBL_FileDef: %s\n", tmpstr); + fprintf(stderr, EDSTABLETOOL_ERROR "dlsym('%s'): %s\n", ModuleTempName, tmpstr); exit(EXIT_FAILURE); } - snprintf(ModuleTempName, sizeof(ModuleTempName), "%s_EDS_TYPEDEF_NAME", CFE_TBL_FileDefPtr->ObjectName); - fptr = dlsym(dlhandle, ModuleTempName); - tmpstr = dlerror(); - if (fptr == NULL || tmpstr != NULL) + *((void **)&DynamicGeneratorFuncPtr) = fptr; + + /* + * If a dynamic generator function was defined, then it must be called now before dlclose(). + * This populates the object in memory (can still be post-processed in Lua too) + */ + if (DynamicGeneratorFuncPtr != NULL) { - if (tmpstr == NULL) - { - tmpstr = "[Object pointer is NULL]"; - } - fprintf(stderr,"Lookup Error on '%s': %s\n", ModuleTempName, tmpstr); - exit(EXIT_FAILURE); + DynamicGeneratorFuncPtr(); + + printf(EDSTABLETOOL_INFO "Got object from C domain, shape=%lux%lu, total size=%lu\n", + (unsigned long)EdsTableTool_Global.CObj.TotalObjectSize / EdsTableTool_Global.CObj.ElementSize, + (unsigned long)EdsTableTool_Global.CObj.ElementSize, + (unsigned long)EdsTableTool_Global.CObj.TotalObjectSize); } - strncpy(EdsTypeName, fptr, sizeof(EdsTypeName) - 1); - EdsTypeName[sizeof(EdsTypeName) - 1] = 0; + dlclose(dlhandle); +} - /* If the table defines a "dynamic content" function, this needs to be executed first */ - snprintf(ModuleTempName, sizeof(ModuleTempName), "%s_GENERATOR", CFE_TBL_FileDefPtr->ObjectName); - fptr = dlsym(dlhandle, ModuleTempName); - tmpstr = dlerror(); - if (fptr == NULL || tmpstr != NULL) +/*---------------------------------------------------------------- + * + * Determine which EDS data type to use for the table encoding. + * + * The tool gets type info from two places, the original C declaration and by + * looking up the table interface in the EDS Database. Ideally they should match + * and then there is no ambiguity. However for a variety of historical reasons, + * the C data type may not exactly match the table interface type, but they must + * be at least compatible. This attempts to determine a common point of compatibility, + * via base type and/or containment relationships. + * + *-----------------------------------------------------------------*/ +EdsLib_Id_t EdsTableTool_FindCommonType(EdsLib_Id_t Type1, EdsLib_Id_t Type2) +{ + EdsLib_DataTypeDB_TypeInfo_t TypeInfo; + EdsLib_DataTypeDB_EntityInfo_t MemberInfo; + EdsLib_Id_t ResultEdsId; + int32_t EdsStatus; + + /* Find some common point between the two types */ + ResultEdsId = Type1; + while (!EdsLib_Is_Similar(ResultEdsId, Type2)) { - if (tmpstr == NULL) + EdsStatus = EdsLib_DataTypeDB_GetTypeInfo(&EDS_DATABASE, ResultEdsId, &TypeInfo); + if (EdsStatus != EDSLIB_SUCCESS) { - tmpstr = "[Object pointer is NULL]"; + ResultEdsId = EDSLIB_ID_INVALID; + break; } - fprintf(stderr,"Lookup Error on '%s': %s\n", ModuleTempName, tmpstr); - exit(EXIT_FAILURE); + + /* one possibility is that the type is a base type of the other type */ + EdsStatus = EdsLib_DataTypeDB_BaseCheck(&EDS_DATABASE, ResultEdsId, Type2); + if (EdsStatus == EDSLIB_SUCCESS) + { + /* In this case use the derived type */ + ResultEdsId = Type2; + break; + } + + /* another possibility is that the table intf type contains/wraps the C data type */ + /* Check the first member type - if its a container, this is the base type, + * or if it is an array, this is the array data type */ + EdsStatus = EdsLib_DataTypeDB_GetMemberByIndex(&EDS_DATABASE, ResultEdsId, 0, &MemberInfo); + if (EdsStatus != EDSLIB_SUCCESS) + { + ResultEdsId = EDSLIB_ID_INVALID; + break; + } + + /* Repeat check using the member type */ + ResultEdsId = MemberInfo.EdsId; } - *((void **)&DynamicGeneratorFuncPtr) = fptr; + return ResultEdsId; +} + +/*---------------------------------------------------------------- + * + * Identifies the object obtained from the C file + * + * This will utilize the "CObj" member of the global, which should + * have been populated by EdsTableTool_LoadCTemplateFile. It looks + * up the type(s) in the EDS DB and attempts to correlate something + * to the objects imported from C. + * + *-----------------------------------------------------------------*/ +void EdsTableTool_IdentifyCObj(void) +{ + EdsTableTool_CObj_t *CObj = &EdsTableTool_Global.CObj; + const char *tmpstr; + char *TempPtr; + size_t tmplen; + char TypeNameBuff[64]; + EdsLib_Id_t TblIntfEdsId; + EdsLib_Id_t AppComponentEdsId; + uint8_t PkgIdx; + int32_t EdsStatus; /* * The TableName in the FileDef object is supposed to be of the form "AppName.TableName" - @@ -259,200 +365,293 @@ void LoadTemplateFile(lua_State *lua, const char *Filename) * if naming conventions are followed. (and if conventions are not followed, this tool * is not likely to work). */ - tmpstr = strchr(CFE_TBL_FileDefPtr->TableName, '.'); + tmpstr = strchr(CObj->FileDefInfo.TableName, '.'); if (tmpstr == NULL) { - RuntimeAppNameLen = 0; - tmpstr = CFE_TBL_FileDefPtr->TableName; + tmplen = 0; + tmpstr = CObj->FileDefInfo.TableName; } else { - RuntimeAppNameLen = tmpstr - CFE_TBL_FileDefPtr->TableName; + tmplen = tmpstr - CObj->FileDefInfo.TableName; ++tmpstr; } - if (RuntimeAppNameLen >= sizeof(RuntimeAppName)) + if (tmplen >= sizeof(CObj->RuntimeAppName)) { - RuntimeAppNameLen = sizeof(RuntimeAppName) - 1; + tmplen = sizeof(CObj->RuntimeAppName) - 1; } - memcpy(RuntimeAppName, CFE_TBL_FileDefPtr->TableName, RuntimeAppNameLen); - RuntimeAppName[RuntimeAppNameLen] = 0; - printf("Runtime App Name: %s\n", RuntimeAppName); + memcpy(CObj->RuntimeAppName, CObj->FileDefInfo.TableName, tmplen); + CObj->RuntimeAppName[tmplen] = 0; - /* - * It is common practice to have an "_APP" suffix to the runtime app name here, - * so this is done as a prefix match. - */ - AppIdx = EDS_MAX_DATASHEETS; - while (true) + strncpy(CObj->TableName, tmpstr, sizeof(CObj->TableName) - 1); + CObj->TableName[sizeof(CObj->TableName) - 1] = 0; + + printf(EDSTABLETOOL_INFO "Identifying C Object: AppName=\'%s\', TableName=\'%s\', CType=\'%s\'\n", + CObj->RuntimeAppName, CObj->TableName, CObj->CTypeName); + + /* Try to determine the EdsId of the C data type. This should exist, but in some cases + * the user has defined a custom type only for the table definition (such as a union). This + * may be OK as long as the custom type is compatible, but this tool cannot verify that */ + tmpstr = CObj->CTypeName; + if (strncmp(CObj->RuntimeAppName, tmpstr, tmplen) == 0 && ispunct((unsigned char)tmpstr[tmplen])) { - --AppIdx; - if (AppIdx == 0) + tmpstr += tmplen + 1; + } + snprintf(TypeNameBuff, sizeof(TypeNameBuff), "%s/%s", CObj->RuntimeAppName, tmpstr); + + /* The C typedef should have a "_t" suffix - trim this off for EDS type names */ + tmplen = strlen(TypeNameBuff); + if (tmplen > 2 && strcmp(&TypeNameBuff[tmplen-2], "_t") == 0) + { + TypeNameBuff[tmplen - 2] = 0; + } + + CObj->CTypeEdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, TypeNameBuff); + if (EdsLib_Is_Valid(CObj->CTypeEdsId)) + { + printf(EDSTABLETOOL_INFO "Found EDS type definition \'%s\' for C datatype \'%s\'\n", TypeNameBuff, CObj->CTypeName); + } + + /* Now find an EDS interface definition and data type that matches */ + AppComponentEdsId = EDSLIB_ID_INVALID; + TblIntfEdsId = EDSLIB_ID_INVALID; + + /* Note that tables will not be loadable by TBL services unless there is an exact match. This + * previously made extra assumptions about the possibility of an _APP suffix and other differences, + * but the FSW is just looking for an exact match, so using other matching schemes is only going + * to defer failure to load time rather than build time */ + EdsStatus = EdsLib_FindPackageIdxByName(&EDS_DATABASE, CObj->RuntimeAppName, &PkgIdx); + if (EdsStatus == EDSLIB_SUCCESS) + { + /* Primary Data type lookup method: Find an interface in the Application section */ + EdsStatus = EdsLib_IntfDB_FindComponentByLocalName(&EDS_DATABASE, PkgIdx, "Application", &AppComponentEdsId); + if (EdsStatus == EDSLIB_SUCCESS) { - /* - * If the naming convention was not followed and no EDS entry is found for this app name, - * then this tool is unlikely work EXCEPT if the user has overloaded the "Description" - * field to refer to a specific EDS DB entry, so this will proceed if that is the case. - */ - EdsAppName = NULL; - EdsAppNameLen = 0; - printf("ERROR: No matching EDS package name for table: \'%s\'\n", CFE_TBL_FileDefPtr->TableName); - break; + EdsStatus = EdsLib_IntfDB_FindComponentInterfaceByLocalName(&EDS_DATABASE, AppComponentEdsId, CObj->TableName, &TblIntfEdsId); } - /* Note that this API does not return NULL, it returns "UNDEF" if the AppIdx is not registered, - * but that should not happen with a static DB and an AppIdx within range */ - EdsId = EDSLIB_MAKE_ID(AppIdx, 1); - EdsAppName = EdsLib_DisplayDB_GetNamespace(&EDS_DATABASE, EdsId); - EdsAppNameLen = strlen(EdsAppName); - if (strncasecmp(EdsAppName, CFE_TBL_FileDefPtr->TableName, EdsAppNameLen) == 0) + /* Fallback - if no exact match, check for a numeric suffix. Some apps (such as MD) + * have multiple instances of the same table and they attach a numeric suffix to the name */ + if (EdsStatus != EDSLIB_SUCCESS) { - if (!isalpha((unsigned char)CFE_TBL_FileDefPtr->TableName[EdsAppNameLen])) + TempPtr = CObj->TableName + strlen(CObj->TableName); + while(TempPtr > CObj->TableName) { - printf("Matched EDS Package Name: %s\n", EdsAppName); - break; + --TempPtr; + if (!isdigit((int)(*TempPtr))) + { + break; + } + + *TempPtr = 0; } + EdsStatus = EdsLib_IntfDB_FindComponentInterfaceByLocalName(&EDS_DATABASE, AppComponentEdsId, CObj->TableName, &TblIntfEdsId); } - } - if (EdsAppName == NULL) - { - fprintf(stderr,"Aborting table build, unidentified application: %s\n", CFE_TBL_FileDefPtr->TableName); - exit(EXIT_FAILURE); - } + if (EdsStatus == EDSLIB_SUCCESS) + { + /* Determine the argument data type for "load" command */ + EdsStatus = EdsLib_IntfDB_FindAllArgumentTypes(&EDS_DATABASE, EDS_TABLE_LOAD_CMD_ID, TblIntfEdsId, &CObj->IntfDataTypeEdsId, 1); + } - /* - * Sometimes the app name is repeated in the table name, so skip it again if so - */ - if (strncmp(RuntimeAppName, tmpstr, RuntimeAppNameLen) == 0) - { - tmpstr += RuntimeAppNameLen; - } - else if (strncmp(EdsAppName, tmpstr, EdsAppNameLen) == 0) - { - tmpstr += EdsAppNameLen; + /* + * If we only got one of the two types (C Datatype or Intf Datatype) then use that. + * (the table may not load correctly but that is a problem elsewhere) + * + * If we got both, and they are equal, then there is also no problem. + * However if we get both an they are different, we have to correlate them somehow. + * The most likely situation is that the C table is an array, and there is possibly + * a separate container around that array. + */ + if (!EdsLib_Is_Valid(CObj->IntfDataTypeEdsId)) + { + /* Intf type not known - just use the C type */ + CObj->SelectedTypeEdsId = CObj->CTypeEdsId; + } + else if (!EdsLib_Is_Valid(CObj->CTypeEdsId)) + { + /* C type not known - just use the intf type */ + CObj->SelectedTypeEdsId = CObj->IntfDataTypeEdsId; + } + else + { + /* see if there is some type that is common between them */ + CObj->SelectedTypeEdsId = EdsTableTool_FindCommonType(CObj->IntfDataTypeEdsId, CObj->CTypeEdsId); + if (!EdsLib_Is_Valid(CObj->SelectedTypeEdsId)) + { + /* try again but reversed */ + CObj->SelectedTypeEdsId = EdsTableTool_FindCommonType(CObj->CTypeEdsId, CObj->IntfDataTypeEdsId); + } + } } - - /* Skip punctuation/name separators */ - while (ispunct((unsigned char) *tmpstr)) + else { - ++tmpstr; + CObj->SelectedTypeEdsId = EDSLIB_ID_INVALID; } - strncpy(TableName, tmpstr, sizeof(TableName) - 1); - TableName[sizeof(TableName) - 1] = 0; - printf("Runtime Table Name: %s\n", TableName); + TypeNameBuff[0] = 0; + EdsLib_DisplayDB_GetTypeName(&EDS_DATABASE, CObj->SelectedTypeEdsId, TypeNameBuff, sizeof(TypeNameBuff)); - /* Now find an EDS type that might match */ - EdsId = EDSLIB_ID_INVALID; + /* sanity check that the type name is valid - it might have returned undefined string */ + if (EdsLib_Is_Valid(EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, TypeNameBuff))) + { + printf(EDSTABLETOOL_INFO "Selected EDS type definition \'%s\' for table \'%s\'\n", TypeNameBuff, CObj->FileDefInfo.TableName); - /* - * Plan A: - * The makefile scripts/tool should have extracted the type name - */ - if (strncmp(EdsTypeName, EdsAppName, EdsAppNameLen) == 0) + strncpy(CObj->EdsTypeName, TypeNameBuff, sizeof(CObj->EdsTypeName) - 1); + CObj->EdsTypeName[sizeof(CObj->EdsTypeName) - 1] = 0; + } + else if (EdsTableTool_Global.AllowPassthrough) { - EdsTypeName[EdsAppNameLen] = '/'; + printf(EDSTABLETOOL_INFO "Allowing passthrough encoding for table \'%s\'\n", CObj->FileDefInfo.TableName); + CObj->EdsTypeName[0] = 0; } - - if (strlen(EdsTypeName) > 2 && strcmp(&EdsTypeName[strlen(EdsTypeName) - 2], "_t") == 0) + else { - EdsTypeName[strlen(EdsTypeName) - 2] = 0; + fprintf(stderr, EDSTABLETOOL_ERROR "Could not map table \'%s\' to an EDS-defined data type\n", CObj->FileDefInfo.TableName); + exit(EXIT_FAILURE); } +} + +/*---------------------------------------------------------------- + * + * Exports a C-defined object to the Lua environment + * + * This expects a lua_State object as the abstract argument, + * and a table should be the topmost entry in the stack. The + * object(s) from C will be wrapped in a Lua object and appended + * to the table. + * + *-----------------------------------------------------------------*/ +void EdsTableTool_AppendObjsFromCObj(lua_State *lua, EdsTableTool_CObj_t *CObj) +{ + EdsLib_Binding_DescriptorObject_t *ObjectUserData; + EdsLib_DataTypeDB_DerivedTypeInfo_t DerivInfo; + int32_t EdsStatus; + const uint8_t *sptr; + void *tptr; + size_t tsz; + int tbl_pos; + size_t bytes_remain; + size_t bytes_per_elem; - printf("Attempting to lookup: %s\n", EdsTypeName); - EdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, EdsTypeName); + /* There should be a table at the top of the initial stack */ + tbl_pos = lua_gettop(lua); - /* - * Plan B: - * If the specific EDS type was indicated in the description field, then use it. - */ - if (!EdsLib_Is_Valid(EdsId) && strchr(CFE_TBL_FileDefPtr->Description, '/') != NULL) + /* now do a sanity check on the size */ + EdsStatus = EdsLib_DataTypeDB_GetDerivedInfo(&EDS_DATABASE, CObj->SelectedTypeEdsId, &DerivInfo); + if (EdsStatus != EDSLIB_SUCCESS) { - strncpy(EdsTypeName, CFE_TBL_FileDefPtr->Description, sizeof(EdsTypeName)); - EdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, EdsTypeName); - } + /* Just directly use the C object data */ + lua_pushlstring(lua, CObj->ObjPtr, CObj->TotalObjectSize); + lua_rawseti(lua, tbl_pos, 1); - /* - * Plan C: - * Attempt to deduce it from the AppName.TableName string - */ - if (!EdsLib_Is_Valid(EdsId) && EdsAppName != NULL) - { - snprintf(EdsTypeName, sizeof(EdsTypeName), "%s/%s", EdsAppName, TableName); - EdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, EdsTypeName); + bytes_per_elem = 0; + bytes_remain = 0; + sptr = NULL; } - - /* as a last-ditch attempt, see if the ObjectName might be in the form of EDSAPP_EDSTYPE */ - if (!EdsLib_Is_Valid(EdsId) && EdsAppName != NULL) + else { - tmpstr = CFE_TBL_FileDefPtr->ObjectName; - if (strncmp(RuntimeAppName, tmpstr, RuntimeAppNameLen) == 0 && ispunct((unsigned char)tmpstr[RuntimeAppNameLen])) + bytes_remain = CObj->TotalObjectSize; + sptr = CObj->ObjPtr; + + if (CObj->ElementSize < CObj->TotalObjectSize && DerivInfo.MaxSize.Bytes <= CObj->ElementSize) { - tmpstr += RuntimeAppNameLen + 1; + /* there are multiple elements, each one gets encoded individually */ + bytes_per_elem = CObj->ElementSize; } - else if (strncmp(EdsAppName, tmpstr, EdsAppNameLen) == 0 && ispunct((unsigned char)tmpstr[EdsAppNameLen])) + else { - tmpstr += EdsAppNameLen + 1; + /* The entire object gets encoded in one go */ + bytes_per_elem = CObj->TotalObjectSize; } - snprintf(EdsTypeName, sizeof(EdsTypeName), "%s/%s", EdsAppName, tmpstr); - EdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, EdsTypeName); } - if (!EdsLib_Is_Valid(EdsId) || EdsLib_DataTypeDB_GetTypeInfo(&EDS_DATABASE, EdsId, &TypeInfo) != EDSLIB_SUCCESS) + lua_getglobal(lua, "EdsDB"); + while (bytes_remain > 0) { - fprintf(stderr,"Error: Cannot get info for table EDS Type Name \'%s\' (id %x)\n", EdsTypeName, (unsigned int)EdsId); - exit(EXIT_FAILURE); - } + /* Call "New_Object" for the object type */ + lua_getfield(lua, -1, "NewObject"); + lua_pushstring(lua, CObj->EdsTypeName); + if (lua_pcall(lua, 1, 1, 1) != LUA_OK) + { + /* note - error handler already displayed error message */ + fprintf(stderr, EDSTABLETOOL_ERROR "Failed to execute: EdsDB.NewObject(%s)\n", CObj->EdsTypeName); + exit(EXIT_FAILURE); + } - printf("--> Using EDS ID 0x%x for \'%s\' data structure, %lu bytes\n",(unsigned int)EdsId,EdsTypeName,(unsigned long)TypeInfo.Size.Bytes); + ObjectUserData = luaL_testudata(lua, -1, "EdsLib_Object"); - if ((CFE_TBL_FileDefPtr->ObjectSize % TypeInfo.Size.Bytes) != 0) - { - fprintf(stderr,"Error: Data size mismatch for EDS Type \'%s\' (id %x): %lu(file)/%lu(EDS)\n", - EdsTypeName, (unsigned int)EdsId, (unsigned long)CFE_TBL_FileDefPtr->ObjectSize, (unsigned long)TypeInfo.Size.Bytes); - exit(EXIT_FAILURE); + /* Fill the Lua object with the data from the C world */ + tsz = EdsLib_Binding_GetBufferMaxSize(ObjectUserData); + EdsLib_LuaBinding_GetNativeObject(lua, -1, &tptr, NULL); + + /* this should not happen because the size was checked earlier */ + if (tsz < bytes_per_elem) + { + fprintf(stderr, EDSTABLETOOL_ERROR "Object of %lu bytes cannot be copied into buffer of %lu bytes (datatype=%s)\n", + (unsigned long)bytes_per_elem, (unsigned long)tsz, CObj->EdsTypeName); + exit(EXIT_FAILURE); + } + + memcpy(tptr, sptr, bytes_per_elem); + sptr += bytes_per_elem; + bytes_remain -= bytes_per_elem; + + /* Append to the table at the top of the initial stack */ + lua_rawseti(lua, tbl_pos, 1 + lua_rawlen(lua, tbl_pos)); } - EdsTableTool_Global.NumMultiple = CFE_TBL_FileDefPtr->ObjectSize / TypeInfo.Size.Bytes; - strncpy(EdsTableTool_Global.EdsTypeName, EdsTypeName, sizeof(EdsTableTool_Global.EdsTypeName) - 1); + /* Reset the stack to the same point as it was initially */ + lua_settop(lua, tbl_pos); +} - lua_getglobal(lua, RuntimeAppName); +/*---------------------------------------------------------------- + * + * Processes the object obtained from the C file + * + * This will utilize the "CObj" member of the global, which should + * have been populated by EdsTableTool_LoadCTemplateFile + * + *-----------------------------------------------------------------*/ +void EdsTableTool_CreateLuaObjFromCObj(lua_State *lua) +{ + EdsTableTool_CObj_t *CObj = &EdsTableTool_Global.CObj; + int obj_idx; + + lua_getglobal(lua, CObj->RuntimeAppName); if (lua_type(lua, -1) != LUA_TTABLE) { lua_pop(lua, 1); lua_newtable(lua); lua_pushvalue(lua, -1); - lua_setglobal(lua, RuntimeAppName); + lua_setglobal(lua, CObj->RuntimeAppName); } lua_newtable(lua); obj_idx = lua_gettop(lua); - lua_pushstring(lua, CFE_TBL_FileDefPtr->TableName); + lua_pushstring(lua, CObj->FileDefInfo.TableName); lua_setfield(lua, obj_idx, "TableName"); /* Set "TableName" in Table object */ - lua_pushstring(lua, CFE_TBL_FileDefPtr->Description); + lua_pushstring(lua, CObj->FileDefInfo.Description); lua_setfield(lua, obj_idx, "Description"); /* Set "Description" in Table object */ - lua_pushstring(lua, CFE_TBL_FileDefPtr->TgtFilename); + lua_pushstring(lua, CObj->FileDefInfo.TgtFilename); lua_setfield(lua, obj_idx, "TgtFilename"); /* Set "TgtFilename" in Table object */ - printf("Loaded Template: %s/%s\n", RuntimeAppName, TableName); - /* - * If a dynamic generator function was defined, then it must be called now before dlclose(). - * This populates the object in memory (can still be post-processed in Lua too) + * If a C object is loaded, make EDS objects out of it */ - if (DynamicGeneratorFuncPtr != NULL) + if (CObj->ObjPtr != NULL) { /* The C object could be an array of entries, so make a table to hold them */ lua_newtable(lua); - DynamicGeneratorFuncPtr(lua); + EdsTableTool_AppendObjsFromCObj(lua, CObj); - printf("--> Got %zu object(s) from C domain\n", lua_rawlen(lua, -1)); + printf(EDSTABLETOOL_INFO "Got %lu object(s) from C domain\n", (unsigned long)lua_rawlen(lua, -1)); /* * If only one object was obtained, then just put that object directly in the parent, @@ -467,44 +666,87 @@ void LoadTemplateFile(lua_State *lua, const char *Filename) lua_settop(lua, obj_idx); } - dlclose(dlhandle); - /* Append the Applist table to hold the loaded table template objects */ lua_rawgetp(lua, LUA_REGISTRYINDEX, &LUA_APPLIST_KEY); lua_pushvalue(lua, obj_idx); lua_rawseti(lua, -2, 1 + lua_rawlen(lua, -2)); - lua_setfield(lua, -2, TableName); /* Set entry in App table */ + lua_settop(lua, obj_idx); + lua_setfield(lua, -2, CObj->TableName); /* Set entry in App table */ lua_pop(lua, 1); + + printf(EDSTABLETOOL_INFO "Stored in global context as %s.%s \n", CObj->RuntimeAppName, CObj->TableName); + + /* now that the object is wrapped in a Lua binding, the original is no longer needed */ + /* this permits another file to be loaded */ + free(CObj->ObjPtr); + memset(&EdsTableTool_Global.CObj, 0, sizeof(EdsTableTool_Global.CObj)); + + lua_settop(lua, obj_idx - 2); } -void LoadLuaFile(lua_State *lua, const char *Filename) +/*---------------------------------------------------------------- + * + * Loads and executes a lua script + * + * The lua script can perform any arbitrary processing of the data objects + * + *-----------------------------------------------------------------*/ +void EdsTableTool_LoadLuaFile(lua_State *lua, const char *Filename) { + printf(EDSTABLETOOL_INFO "Executing LUA script: %s\n", Filename); + if (luaL_loadfile(lua, Filename) != LUA_OK) { - fprintf(stderr,"Cannot load Lua file: %s: %s\n", Filename, lua_tostring(lua, -1)); + fprintf(stderr, EDSTABLETOOL_ERROR "Cannot load Lua file: %s: %s\n", Filename, lua_tostring(lua, -1)); exit(EXIT_FAILURE); } - printf("Executing LUA: %s\n", Filename); + if (lua_pcall(lua, 0, 0, 1) != LUA_OK) { /* note - error handler already displayed error message */ - fprintf(stderr, "Failed to execute: %s\n", Filename); + fprintf(stderr, EDSTABLETOOL_ERROR "Failed to execute: %s\n", Filename); exit(EXIT_FAILURE); } } -void PushEncodedSingleObject(lua_State *lua) +/*---------------------------------------------------------------- + * + * Encodes the singular Lua object at the top of the stack + * + * Input stack should have a single native object at the top (-1) + * That object will be removed and replaced with the encoded representation + * + *-----------------------------------------------------------------*/ +void EdsTableTool_PushEncodedSingleObject(lua_State *lua) { - luaL_checkudata(lua, -1, "EdsLib_Object"); - lua_getglobal(lua, "EdsDB"); - lua_getfield(lua, -1, "Encode"); - lua_remove(lua, -2); /* remove the EdsDB global */ - lua_pushvalue(lua, -2); - lua_call(lua, 1, 1); + if (luaL_testudata(lua, -1, "EdsLib_Object")) + { + lua_getglobal(lua, "EdsDB"); + lua_getfield(lua, -1, "Encode"); + lua_remove(lua, -2); /* remove the EdsDB global */ + lua_pushvalue(lua, -2); + lua_call(lua, 1, 1); + } + else + { + luaL_checkstring(lua, -1); + } } -void PushEncodedMultiObject(lua_State *lua) +/*---------------------------------------------------------------- + * + * Encodes the Lua table object at the top of the stack + * + * Input stack should have a Lua table containing an array/list of native objects + * That object will be removed, each element will be individually encoded by + * invoking EdsTableTool_PushEncodedSingleObject, then all the resulting binary + * blobs will be concatenated together into a single blob. + * + * The final result will be put on the stack, replacing the original table. + * + *-----------------------------------------------------------------*/ +void EdsTableTool_PushEncodedMultiObject(lua_State *lua) { size_t num_tbl; size_t num_enc; @@ -521,25 +763,84 @@ void PushEncodedMultiObject(lua_State *lua) { ++num_enc; lua_rawgeti(lua, obj_idx, num_enc); /* Get the EdsLib_Object table entry */ - PushEncodedSingleObject(lua); + EdsTableTool_PushEncodedSingleObject(lua); lua_remove(lua, -2); /* remove the EdsLib_Object table entry from stack */ } lua_concat(lua, num_enc); } -int Write_GenericFile(lua_State *lua) +/*---------------------------------------------------------------- + * + * Opens a file for output + * + * This is just a wrapper around fopen() that applies the OUTPUT_DIR + * + * The "filename_pos" should refer to a stack position containing a + * relative file name + * + * If the "OUTPUT_DIR" global is set, it will create a full path using + * the specified directory combined with the file name. If the + * "OUTPUT_DIR" is not set, then the full path will be based on + * the current directory (.) + * + * In both cases the file is opened for writing ("w" mode) and the + * file pointer is returned. + * + *-----------------------------------------------------------------*/ +FILE *EdsTableTool_OpenOutputFile(lua_State *lua, int filename_pos) { FILE *OutputFile = NULL; - const char *OutputName = luaL_checkstring(lua, 1); + int start_top = lua_gettop(lua); + const char *OutputName = luaL_checkstring(lua, filename_pos); + char OutputFullNameBuffer[256]; + const char *OutputPath; + + if (OutputName != NULL) + { + lua_getglobal(lua, "OUTPUT_DIR"); + if (lua_isnoneornil(lua, -1)) + { + OutputPath = "."; + } + else + { + OutputPath = lua_tostring(lua, -1); + } + + snprintf(OutputFullNameBuffer, sizeof(OutputFullNameBuffer), "%s/%s", OutputPath, OutputName); + OutputFile = fopen(OutputFullNameBuffer, "w"); + } + + lua_settop(lua, start_top); + + return OutputFile; +} + +/*---------------------------------------------------------------- + * + * Writes an encoded object to a file (generic) + * + * The stack should contain a single native (unencoded) object at the top + * That object will be encoded to binary, and written to the output file. + * + * There is no additional headers or framing added to the object; the file + * will contain only the binary data from the object and nothing else. + * + * The filename is retrieved from absolute stack position 1. + * + *-----------------------------------------------------------------*/ +int EdsTableTool_WriteGenericFile(lua_State *lua) +{ + FILE *OutputFile; - OutputFile = fopen(OutputName, "w"); + OutputFile = EdsTableTool_OpenOutputFile(lua, 1); if (OutputFile == NULL) { - return luaL_error(lua, "%s: %s", OutputName, strerror(errno)); + return luaL_error(lua, "%s: %s", lua_tostring(lua, 1), strerror(errno)); } - PushEncodedSingleObject(lua); + EdsTableTool_PushEncodedSingleObject(lua); fwrite(lua_tostring(lua, -1), lua_rawlen(lua, -1), 1, OutputFile); fclose(OutputFile); lua_pop(lua, 1); @@ -547,7 +848,21 @@ int Write_GenericFile(lua_State *lua) return 0; } -int Write_CFE_EnacapsulationFile(lua_State *lua) +/*---------------------------------------------------------------- + * + * Writes an encoded object to a file (CFE table) + * + * The stack should contain a native (unencoded) object or table at the top + * That object will be encoded to binary, and written to the output file. + * + * This function writes a CFE_FS header and a CFE_TBL header to the file, + * then writes the encoded data. The resulting file is intended to be + * compatible with the CFE Table load. + * + * The filename is retrieved from absolute stack position 1. + * + *-----------------------------------------------------------------*/ +int EdsTableTool_WriteCfeTableFile(lua_State *lua) { union { @@ -559,22 +874,22 @@ int Write_CFE_EnacapsulationFile(lua_State *lua) uint32_t TblHeaderBlockSize; uint32_t FileHeaderBlockSize; EdsLib_Id_t PackedEdsId; - FILE *OutputFile = NULL; - const char *OutputName = luaL_checkstring(lua, 1); + FILE *OutputFile; + const char *OutputName; EdsLib_DataTypeDB_TypeInfo_t BlockInfo; const void *content_ptr; size_t content_sz; - PackedEdsId = EdsLib_DisplayDB_LookupTypeName(&EDS_DATABASE, EdsTableTool_Global.EdsTypeName); - printf("Generating file using EDS ID: %x\n", (unsigned int)PackedEdsId); + OutputFile = NULL; + OutputName = luaL_checkstring(lua, 1); if (lua_type(lua, -1) == LUA_TTABLE) { - PushEncodedMultiObject(lua); + EdsTableTool_PushEncodedMultiObject(lua); } else { - PushEncodedSingleObject(lua); + EdsTableTool_PushEncodedSingleObject(lua); } if (lua_isnoneornil(lua, -1)) @@ -583,12 +898,10 @@ int Write_CFE_EnacapsulationFile(lua_State *lua) } memset(&Buffer, 0, sizeof(Buffer)); - Buffer.TblHeader.EdsAppId = EdsLib_Get_AppIdx(PackedEdsId); - Buffer.TblHeader.EdsFormatId = EdsLib_Get_FormatIdx(PackedEdsId); Buffer.TblHeader.NumBytes = lua_rawlen(lua, -1); strncpy(Buffer.TblHeader.TableName, luaL_checkstring(lua, 3), sizeof(Buffer.TblHeader.TableName)); - PackedEdsId = EDSLIB_MAKE_ID(EDS_INDEX(CFE_TBL), CFE_TBL_File_Hdr_DATADICTIONARY); + PackedEdsId = EDSLIB_MAKE_ID(EDS_INDEX(CFE_TBL), EdsContainer_CFE_TBL_File_Hdr_DATADICTIONARY); EdsLib_DataTypeDB_PackCompleteObject(&EDS_DATABASE, &PackedEdsId, PackedTblHeader, &Buffer.TblHeader, sizeof(PackedTblHeader) * 8, sizeof(Buffer.TblHeader)); EdsLib_DataTypeDB_GetTypeInfo(&EDS_DATABASE, PackedEdsId, &BlockInfo); @@ -600,13 +913,13 @@ int Write_CFE_EnacapsulationFile(lua_State *lua) Buffer.FileHeader.Length = TblHeaderBlockSize + lua_rawlen(lua, -1); snprintf(Buffer.FileHeader.Description, sizeof(Buffer.FileHeader.Description), "%s", luaL_checkstring(lua, 2)); - PackedEdsId = EDSLIB_MAKE_ID(EDS_INDEX(CFE_FS), CFE_FS_Header_DATADICTIONARY); + PackedEdsId = EDSLIB_MAKE_ID(EDS_INDEX(CFE_FS), EdsContainer_CFE_FS_Header_DATADICTIONARY); EdsLib_DataTypeDB_PackCompleteObject(&EDS_DATABASE, &PackedEdsId, PackedFileHeader, &Buffer, sizeof(PackedFileHeader) * 8, sizeof(Buffer)); EdsLib_DataTypeDB_GetTypeInfo(&EDS_DATABASE, PackedEdsId, &BlockInfo); FileHeaderBlockSize = (BlockInfo.Size.Bits + 7) / 8; - OutputFile = fopen(OutputName, "w"); + OutputFile = EdsTableTool_OpenOutputFile(lua, 1); if (OutputFile == NULL) { return luaL_error(lua, "%s: %s", OutputName, strerror(errno)); @@ -622,16 +935,67 @@ int Write_CFE_EnacapsulationFile(lua_State *lua) } else { - fprintf(stderr, "WARNING: No content produced\n"); + fprintf(stderr, EDSTABLETOOL_WARNING "No content produced\n"); } + fclose(OutputFile); - printf("Wrote File: %s\n", OutputName); + printf(EDSTABLETOOL_INFO "Wrote File: %s\n", OutputName); lua_pop(lua, 1); return 0; } -static int ErrorHandler(lua_State *lua) +/*---------------------------------------------------------------- + * + * Gets the template object metadata from the C domain + * + * Retrieves the lua table associated with the specified table name (arg 1) + * + * This is the meta object that was created by EdsTableTool_CreateLuaObjFromCObj() + * and is used when saving the file associated with this template object. + * + *-----------------------------------------------------------------*/ +static int EdsTableTool_GetCObjectMetaData(lua_State *lua) +{ + int nret = 0; + int tpos; + int tidx; + + /* This registry entry is a list of objects created from .so files */ + /* See EdsTableTool_CreateLuaObjFromCObj() for how it is assembled */ + lua_rawgetp(lua, LUA_REGISTRYINDEX, &LUA_APPLIST_KEY); + tidx = lua_gettop(lua); + tpos = lua_rawlen(lua, tidx); + while (tpos > 0) + { + lua_rawgeti(lua, tidx, tpos); + if (lua_type(lua, -1) == LUA_TTABLE) + { + lua_getfield(lua, -1, "TableName"); + if (lua_compare(lua, -1, 1, LUA_OPEQ)) + { + /* found a match, return it */ + lua_settop(lua, tidx + 1); + nret = 1; + break; + } + } + + lua_settop(lua, tidx); + --tpos; + } + + return nret; +} + +/*---------------------------------------------------------------- + * + * Handles errors in processing the objects from Lua + * + * Reports an error to the console, along with a Lua backtrace + * + *-----------------------------------------------------------------*/ +static int EdsTableTool_ErrorHandler(lua_State *lua) { lua_getglobal(lua, "debug"); lua_getfield(lua, -1, "traceback"); @@ -639,10 +1003,15 @@ static int ErrorHandler(lua_State *lua) lua_pushnil(lua); lua_pushinteger(lua, 3); lua_call(lua, 2, 1); - fprintf(stderr, "Error: %s\n%s\n", lua_tostring(lua, 1), lua_tostring(lua, 2)); + fprintf(stderr, EDSTABLETOOL_ERROR "%s\n%s\n", lua_tostring(lua, 1), lua_tostring(lua, 2)); return 0; } +/*---------------------------------------------------------------- + * + * Main entry point to the tool + * + *-----------------------------------------------------------------*/ int main(int argc, char *argv[]) { int arg; @@ -657,20 +1026,24 @@ int main(int argc, char *argv[]) luaL_openlibs(lua); /* Stack index 1 will be the error handler function (used for protected calls) */ - lua_pushcfunction(lua, ErrorHandler); + lua_pushcfunction(lua, EdsTableTool_ErrorHandler); /* Create an Applist table to hold the loaded table template objects */ lua_newtable(lua); lua_rawsetp(lua, LUA_REGISTRYINDEX, &LUA_APPLIST_KEY); - while ((arg = getopt (argc, argv, "e:")) != -1) + while ((arg = getopt (argc, argv, "e:p")) != -1) { switch (arg) { + case 'p': + EdsTableTool_Global.AllowPassthrough = true; + break; + case 'e': if (luaL_loadstring(lua, optarg) != LUA_OK) { - fprintf(stderr,"ERROR: Invalid command line expression: %s\n", + fprintf(stderr, EDSTABLETOOL_ERROR "Invalid command line expression: %s\n", luaL_tolstring(lua,-1, NULL)); lua_pop(lua, 2); return EXIT_FAILURE; @@ -678,7 +1051,7 @@ int main(int argc, char *argv[]) if (lua_pcall(lua, 0, 0, 1) != LUA_OK) { - fprintf(stderr,"ERROR: Cannot evaluate command line expression: %s\n", + fprintf(stderr, EDSTABLETOOL_ERROR "Cannot evaluate command line expression: %s\n", luaL_tolstring(lua,-1, NULL)); lua_pop(lua, 2); return EXIT_FAILURE; @@ -688,11 +1061,11 @@ int main(int argc, char *argv[]) case '?': if (isprint (optopt)) { - fprintf(stderr, "Unknown option `-%c'.\n", optopt); + fprintf(stderr, EDSTABLETOOL_ERROR "Unknown option `-%c'.\n", optopt); } else { - fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt); + fprintf(stderr, EDSTABLETOOL_ERROR "Unknown option character `\\x%x'.\n", optopt); } return EXIT_FAILURE; break; @@ -706,7 +1079,7 @@ int main(int argc, char *argv[]) /* All additional args are input files. If none, this should be considered an error */ if (optind >= argc) { - fprintf(stderr, "ERROR: No input files specified on command line.\n"); + fprintf(stderr, EDSTABLETOOL_ERROR "No input files specified on command line.\n"); return EXIT_FAILURE; } @@ -716,41 +1089,38 @@ int main(int argc, char *argv[]) lua_getglobal(lua, "CPUNUMBER"); if (lua_isstring(lua, -2) && lua_isnil(lua, -1)) { - lua_pushinteger(lua, CFE_MissionLib_GetInstanceNumber(&CFE_SOFTWAREBUS_INTERFACE, - lua_tostring(lua, -2))); - lua_setglobal(lua, "CPUNUMBER"); + uint16_t InstanceNum; + + InstanceNum = CFE_MissionLib_GetInstanceNumber(&CFE_SOFTWAREBUS_INTERFACE, lua_tostring(lua, -2)); + if (InstanceNum > 0) + { + lua_pushinteger(lua, InstanceNum); + lua_setglobal(lua, "CPUNUMBER"); + } } else if (lua_isnumber(lua, -1) && lua_isnil(lua, -2)) { - char TempBuf[64]; - lua_pushstring(lua, CFE_MissionLib_GetInstanceName(&CFE_SOFTWAREBUS_INTERFACE, - lua_tointeger(lua, -1), TempBuf, sizeof(TempBuf))); - lua_setglobal(lua, "CPUNAME"); + const char *NameStr; + + NameStr = CFE_MissionLib_GetInstanceNameNonNull(&CFE_SOFTWAREBUS_INTERFACE, lua_tointeger(lua, -1)); + if (NameStr != NULL) + { + lua_pushstring(lua, NameStr); + lua_setglobal(lua, "CPUNAME"); + } } lua_pop(lua, 2); - /* - * if the SIMULATION compile-time directive is set, create a global - * Lua variable with that name so Lua code can do "if SIMULATION" statements - * as needed to support simulated environments. - * - * Note that a double macro is needed here, to force expansion of the macro - * rather than stringifying the macro name itself i.e. "SIMULATION" - */ -#ifdef SIMULATION -#define EDS2CFETBL_STRINGIFY(x) #x -#define EDS2CFETBL_SIMSTRING(x) EDS2CFETBL_STRINGIFY(x) - lua_pushstring(lua, EDS2CFETBL_SIMSTRING(SIMULATION)); - lua_setglobal(lua, "SIMULATION"); -#endif - lua_pushinteger(lua, CFE_FS_SubType_TBL_IMG); - lua_pushcclosure(lua, Write_CFE_EnacapsulationFile, 1); + lua_pushcclosure(lua, EdsTableTool_WriteCfeTableFile, 1); lua_setglobal(lua, "Write_CFE_LoadFile"); - lua_pushcfunction(lua, Write_GenericFile); + lua_pushcfunction(lua, EdsTableTool_WriteGenericFile); lua_setglobal(lua, "Write_GenericFile"); + lua_pushcfunction(lua, EdsTableTool_GetCObjectMetaData); + lua_setglobal(lua, "Get_CObjectMetaData"); + EdsLib_Lua_Attach(lua, &EDS_DATABASE); CFE_MissionLib_Lua_SoftwareBus_Attach(lua, &CFE_SOFTWAREBUS_INTERFACE); lua_setglobal(lua, "EdsDB"); @@ -765,15 +1135,24 @@ int main(int argc, char *argv[]) slen = strlen(argv[arg]); if (slen >= 3 && strcmp(argv[arg] + slen - 3, ".so") == 0) { - LoadTemplateFile(lua, argv[arg]); + /* Handling a C table object (.so file) is a multi-step process */ + /* the first step is to populate the "CObj" member in the global */ + EdsTableTool_LoadCTemplateFile(argv[arg]); + + /* next step is to identify that object */ + EdsTableTool_IdentifyCObj(); + + /* now create Lua object(s) from those C objects */ + /* note: future lua scripts may act on these objects */ + EdsTableTool_CreateLuaObjFromCObj(lua); } else if (slen >= 4 && strcmp(argv[arg] + slen - 4, ".lua") == 0) { - LoadLuaFile(lua, argv[arg]); + EdsTableTool_LoadLuaFile(lua, argv[arg]); } else { - fprintf(stderr,"WARNING: Unable to handle file argument: %s\n",argv[arg]); + fprintf(stderr, EDSTABLETOOL_ERROR "Unable to handle file argument: %s\n",argv[arg]); return EXIT_FAILURE; } } @@ -797,7 +1176,7 @@ int main(int argc, char *argv[]) if (lua_pcall(lua, 4, 0, 1) != LUA_OK) { /* note - more descriptive error message already printed by error handler function */ - fprintf(stderr, "Failed to write CFE table file from template\n"); + fprintf(stderr, EDSTABLETOOL_ERROR "Failed to write CFE table file from template\n"); return (EXIT_FAILURE); } diff --git a/cfecfs/eds2cfetbl/scripts/add_cfe_tables_impl.cmake b/cfecfs/eds2cfetbl/scripts/add_cfe_tables_impl.cmake index df510c8..c791061 100644 --- a/cfecfs/eds2cfetbl/scripts/add_cfe_tables_impl.cmake +++ b/cfecfs/eds2cfetbl/scripts/add_cfe_tables_impl.cmake @@ -32,14 +32,14 @@ function(do_add_cfe_tables_impl TABLE_FQNAME) set(TABLE_GENSCRIPT "${CFS_TABLETOOL_SCRIPT_DIR}/generate_eds_table_rules.cmake") set(TABLE_CMD_OPTS - -DTEMPLATE_FILE="${TEMPLATE_FILE}" - -DAPP_NAME="${ADDTBL_ARG_APP_NAME}" - -DTARGET_NAME="${ADDTBL_ARG_TARGET_NAME}" + -DTEMPLATE_FILE=${TEMPLATE_FILE} + -DAPP_NAME=${ADDTBL_ARG_APP_NAME} + -DTARGET_NAME=${ADDTBL_ARG_TARGET_NAME} ) if (ADDTBL_ARG_INSTALL_SUBDIR) list(APPEND TABLE_CMD_OPTS - -DINSTALL_SUBDIR="${ADDTBL_ARG_INSTALL_SUBDIR}" + -DINSTALL_SUBDIR=${ADDTBL_ARG_INSTALL_SUBDIR} ) endif() @@ -51,13 +51,13 @@ function(do_add_cfe_tables_impl TABLE_FQNAME) ${ADDTBL_ARG_APP_NAME}.table ) list(APPEND TABLE_CMD_OPTS - -DINCLUDE_DIRS="$" - -DCOMPILE_DEFS="$" + -DINCLUDE_DIRS=$ + -DCOMPILE_DEFS=$ ) else() list(APPEND TABLE_CMD_OPTS - -DINCLUDE_DIRS="$" - -DCOMPILE_DEFS="$" + -DINCLUDE_DIRS=$ + -DCOMPILE_DEFS=$ ) endif() @@ -111,17 +111,18 @@ function(do_add_cfe_tables_impl TABLE_FQNAME) OUTPUT "${TABLE_RULEFILE}" COMMAND ${CMAKE_COMMAND} ${TABLE_CMD_OPTS} - -DOUTPUT_FILE="${TABLE_RULEFILE}" - -DTABLE_NAME="${TABLE_BASENAME}" - -DSOURCES="${TBL_SRC}" - -DOBJEXT="${CMAKE_C_OUTPUT_EXTENSION}" - -P "${TABLE_GENSCRIPT}" + "-DOUTPUT_FILE=${TABLE_RULEFILE}" + "-DTABLE_NAME=${TABLE_BASENAME}" + "-DSOURCES=${TBL_SRC}" + "-DOBJEXT=${CMAKE_C_OUTPUT_EXTENSION}" + -P ${TABLE_GENSCRIPT} WORKING_DIRECTORY ${MISSION_BINARY_DIR}/tables DEPENDS ${TABLE_TEMPLATE} ${TABLE_GENSCRIPT} ${TABLE_DEPENDENCIES} + VERBATIM ) # Add a custom target to generate the config file diff --git a/cfecfs/eds2cfetbl/scripts/eds2cfetbl_rules.mk b/cfecfs/eds2cfetbl/scripts/eds2cfetbl_rules.mk index d2c325e..ab0580b 100644 --- a/cfecfs/eds2cfetbl/scripts/eds2cfetbl_rules.mk +++ b/cfecfs/eds2cfetbl/scripts/eds2cfetbl_rules.mk @@ -1,43 +1,79 @@ # Rules for EDS-based CFE table generation -# the basic set of flags to pass to the table tool -TBLTOOL_FLAGS += -e MISSION_DEFS=\"$(MISSION_DEFS)\" -TBLTOOL_FLAGS += -e CPUNAME=\"$(CFE_TABLE_CPUNAME)\" -TBLTOOL_FLAGS += -e APPNAME=\"$(CFE_TABLE_APPNAME)\" -TBLTOOL_FLAGS += -e TABLENAME=\"$(CFE_TABLE_BASENAME)\" +C_INCLUDE_DIRS += $(MISSION_BINARY_DIR)/inc LOCAL_CFLAGS += $(addprefix -D,$(C_COMPILE_DEFS)) LOCAL_CFLAGS += $(addprefix -I,$(C_INCLUDE_DIRS)) LOCAL_CFLAGS += -DCFE_TABLE_NAME="$(CFE_TABLE_BASENAME)" -AWK ?= /usr/bin/awk -M4 ?= /usr/bin/m4 +AWK ?= awk +M4 ?= m4 -.PRECIOUS: eds/%.c +DEPENDENCY_FLAGS = -MMD -MF "$(subst /,_,$(@)).d" -MT "$(@)" +SRC = $(shell cat eds/$(*).source) +OBJNAME = $(shell cat eds/$(*).objname) +TYPENAME = $(shell cat eds/$(*).typename) -# This needs to strip comments from the original C file - -# the best way to do that is to send it through the C preprocessor (-E switch) -# NOTE: by defining CFE_TBL_FILEDEF_H here, we effectively squelch the normal CFE definition of the macro. -# this way we can use our own macro definition instead, and actually evaluate it several different ways. +TBLDEF_C = eds/$(*).tbldef.c +OBJDEF_C = eds/$(*).objdef.c +FILEDEF_C = eds/$(*).filedef.c + +.PRECIOUS: eds/%.c eds/%.source eds/%.tbldef.c eds/%.objname eds/%.typename + +# The "source" just contains the complete path to the C table file eds/%.source: @mkdir -pv $(dir $(@)) echo "$(<)" > "$(@)" -eds/%.filedef: SRC=$(shell cat $(@:.filedef=.source)) -eds/%.filedef: eds/%.source $(TABLETOOL_SCRIPT_DIR)/extract_filedef.awk - $(CC) -E -MMD -MF "$(notdir $(@)).d" -MT "$(@)" -DCFE_TBL_FILEDEF_H $(CFLAGS) $(LOCAL_CFLAGS) -o "$(@).tmp" "$(SRC)" - $(AWK) -v SRC="$(SRC)" -f "$(TABLETOOL_SCRIPT_DIR)/extract_filedef.awk" "$(@).tmp" > "$(@).out" - mv "$(@).out" "$(@)" - rm -f "$(@).tmp" - -eds/%.objname: eds/%.filedef $(TABLETOOL_SCRIPT_DIR)/extract_varname.m4 - $(M4) -P "$(TABLETOOL_SCRIPT_DIR)/extract_varname.m4" "$(<)" > "$(@)" - -eds/%.c: SRC=$(shell cat $(@:.c=.source)) -eds/%.c: OBJNAME=$(shell cat $(@:.c=.objname)) -eds/%.c: eds/%.objname $(TABLETOOL_SCRIPT_DIR)/extract_object.awk - $(AWK) -v SRC="$(SRC)" -v OBJNAME="$(OBJNAME)" -f "$(TABLETOOL_SCRIPT_DIR)/extract_object.awk" "$(SRC)" > "$(@).out" - mv "$(@).out" "$(@)" +# The "tbldef.c" artifact contains only the preprocessed content of the table definition C file +# the key is to pipe it through the C preprocessor so it handles all #include and #ifdef properly, +# then strip out all content from files other than the directly-supplied file. Importantly, this +# will resolve any C macros used in that file, including CFE_TBL_FILEDEF, leaving only the real definition +eds/%.tbldef.c: eds/%.source $(TABLETOOL_SCRIPT_DIR)/extract_filedef.awk + $(CC) -E $(DEPENDENCY_FLAGS) $(CFLAGS) $(LOCAL_CFLAGS) "$(SRC)" |\ + $(AWK) -v SRC="$(SRC)" -f "$(TABLETOOL_SCRIPT_DIR)/extract_filedef.awk" > "$(@).tmp" + mv "$(@).tmp" "$(@)" + +# The "filedef.c" contains only the C FILEDEF macro/object, not the defintion of that object +# Because it is separating the filedef from the object definition, it also needs to remove "sizeof" refs +eds/%.filedef.c: eds/%.tbldef.c $(TABLETOOL_SCRIPT_DIR)/separate_filedef.awk + $(AWK) -v WANT_FILEDEF=1 -f "$(TABLETOOL_SCRIPT_DIR)/separate_filedef.awk" "$(<)" |\ + $(M4) -P -E -D sizeof=0 > "$(@).tmp" + mv "$(@).tmp" "$(@)" + +# The "objdef.c" contains only the C object definition/instantiation (not the FILEDEF macro/object) +eds/%.objdef.c: eds/%.tbldef.c $(TABLETOOL_SCRIPT_DIR)/separate_filedef.awk + $(AWK) -v WANT_FILEDEF=0 -f "$(TABLETOOL_SCRIPT_DIR)/separate_filedef.awk" "$(<)" > "$(@).tmp" + mv "$(@).tmp" "$(@)" + +filedef_extract.o: $(TABLETOOL_SCRIPT_DIR)/filedef_extract.c + $(CC) $(DEPENDENCY_FLAGS) $(CFLAGS) $(LOCAL_CFLAGS) -c -o "$(@)" "$(<)" + +eds/%.filedef.o: eds/%.filedef.c + $(CC) $(DEPENDENCY_FLAGS) -include cfe_tbl_filedef.h $(CFLAGS) $(LOCAL_CFLAGS) -c -o "$(@)" "$(<)" + +# The "objname" file will contain strictly the C object name used in the table definition +# This is a challenge to get because it only is defined in the FileDef object as a string, so +# we must compile the object in isolation and then run a program which prints the string +eds/%.objname: eds/%.filedef.o filedef_extract.o + $(CC) $(DEPENDENCY_FLAGS) $(CFLAGS) $(LOCAL_CFLAGS) -o "$(@).getter" $(^) + $(@).getter > "$(@)" + +# The "typename" file will contain strictly the C data type name used in the table definition +eds/%.typename: eds/%.objname eds/%.objdef.c + $(AWK) -v OBJNAME="$(OBJNAME)" -f "$(TABLETOOL_SCRIPT_DIR)/extract_typename.awk" "$(OBJDEF_C)" > "$(@).tmp" + mv "$(@).tmp" "$(@)" + +# This generates a .c file which "wraps" the original definition and puts it into a function +# This allows the structure initializers to use constructs that are only resolvable at runtime +# The trick here is grab all the preprocessor directives (particularly #include) +# and keep them all at the start outside of the wrapper function, because many of +# these will not work correctly inside a function, and they are necessary to get the typedefs +eds/%.c: eds/%.source eds/%.tbldef.c eds/%.objname eds/%.typename $(TABLETOOL_SCRIPT_DIR)/table_wrapper.c.m4 + grep -E '^\s*#' "$(SRC)" > "$(@).tmp" + $(M4) -P -E -D TBLDEF_C="$(TBLDEF_C)" -D OBJNAME="$(OBJNAME)" -D TYPENAME=$(TYPENAME) \ + $(TABLETOOL_SCRIPT_DIR)/table_wrapper.c.m4 >> "$(@).tmp" + mv "$(@).tmp" "$(@)" eds/%.o: eds/%.c $(TABLETOOL_SCRIPT_DIR)/eds_tbltool_filedef.h $(CC) -Wall -Werror -fPIC -g $(CFLAGS) $(LOCAL_CFLAGS) -include "$(TABLETOOL_SCRIPT_DIR)/eds_tbltool_filedef.h" -o "$(@)" -c "$(<)" diff --git a/cfecfs/eds2cfetbl/scripts/eds_tbltool_filedef.h b/cfecfs/eds2cfetbl/scripts/eds_tbltool_filedef.h index 62bc0f5..9f39f90 100644 --- a/cfecfs/eds2cfetbl/scripts/eds_tbltool_filedef.h +++ b/cfecfs/eds2cfetbl/scripts/eds_tbltool_filedef.h @@ -10,7 +10,7 @@ EdsDataType_CFE_SB_MsgIdValue_t CFE_SB_CmdTopicIdToMsgId(uint16_t TopicId, uint1 EdsDataType_CFE_SB_MsgIdValue_t CFE_SB_TlmTopicIdToMsgId(uint16_t TopicId, uint16_t InstanceNum); uint16_t EdsTableTool_GetProcessorId(void); -void EdsTableTool_DoExport(void *arg, const void *filedefptr, const void *obj, size_t sz); +void EdsTableTool_DoExport(const void *filedefptr, const void *objptr, size_t objsize, const char *typename, size_t typesize); static inline EdsDataType_CFE_SB_MsgIdValue_t CFE_SB_GlobalCmdTopicIdToMsgId(uint16_t TopicId) diff --git a/cfecfs/eds2cfetbl/scripts/extract_filedef.awk b/cfecfs/eds2cfetbl/scripts/extract_filedef.awk index 42634a3..314321b 100644 --- a/cfecfs/eds2cfetbl/scripts/extract_filedef.awk +++ b/cfecfs/eds2cfetbl/scripts/extract_filedef.awk @@ -4,16 +4,7 @@ BEGIN { RS="\n"; ORS="\n"; FS=" "; - IS_FILEDEF_MACRO=0; # set nonzero if the CFE_TBL_FILEDEF macro is found - IS_FILEDEF_DIRECT=0; # set nonzero if the CFE_TBL_FileDef_t is instantiated directly (discouraged) - DIRECT_CONTENT=""; -} - -END { - if (IS_FILEDEF_MACRO >= 0) { - print "\nERROR: Unable to extract CFE_TBL_FILEDEF macro from: " SRC "\n" > "/dev/stderr" - exit 1 - } + IS_FILEDEF=0; # set nonzero at the start of the filedef } # The C preprocessor outputs lines starting with # that indicate which file it came from @@ -24,43 +15,7 @@ END { next; } -# Ignore anything that is not in the original source file -FILE!="\"" SRC "\"" { - next; -} - -# Filedef starts at the CFE_TBL_FILEDEF macro -IS_FILEDEF_MACRO==0 && /\/ { - IS_FILEDEF_MACRO=1 - IS_FILEDEF_DIRECT=-1 -} - -IS_FILEDEF_DIRECT==0 { - if ($1 == "CFE_TBL_FileDef_t") { - IS_FILEDEF_DIRECT=1 - IS_FILEDEF_MACRO=-1 - } -} - -IS_FILEDEF_DIRECT==1 { - DIRECT_CONTENT=DIRECT_CONTENT $0 - if ($0 ~ /;/) { - if (match(DIRECT_CONTENT, /{(.*)}/, OUT1) == 0) { - print "\nERROR: Unable to extract CFE_TBL_FILEDEF info from direct instatiation: " DIRECT "\n" > "/dev/stderr" - exit 1 - } - # now that the CFE_TBL_FileDef_t content is identified, it first needs to be split at commas - split(OUT1[1], OUT2, ",") - # Then the first item taken out (only the first for now) - match(OUT2[1], /"(.*)"/, OUT3) - print "CFE_TBL_FILEDEF(" OUT3[1] ")" - IS_FILEDEF_DIRECT=-1 - } -} - -# send the "CFE_TBL_FILEDEF" macro content to the output -IS_FILEDEF_MACRO==1 { +# Pass through anything that is in the original source file +FILE=="\"" SRC "\"" { print - if ($0 ~ /\)/) - IS_FILEDEF_MACRO=-1 } diff --git a/cfecfs/eds2cfetbl/scripts/extract_object.awk b/cfecfs/eds2cfetbl/scripts/extract_object.awk deleted file mode 100644 index e9ea1d4..0000000 --- a/cfecfs/eds2cfetbl/scripts/extract_object.awk +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/awk -f - -BEGIN { - IS_DECL=0 -} - -END { - if (DATATYPE) { - print "const char " OBJNAME "_EDS_TYPEDEF_NAME[] = \"" DATATYPE "\";\n" - } - else { - print "extract_object: Error: No instantiation of " OBJNAME " found in source file" > "/dev/stderr"; - exit 1; - } - - print "\n" - print "void " OBJNAME "_GENERATOR(void *arg)" - print "{\n" - print "#include \"" SRC "\"\n" - print "EdsTableTool_DoExport(arg, &CFE_TBL_FileDef, &" OBJNAME ", sizeof(" OBJNAME "));" - print "}" -} - -# This preserves the original "CFE_TBL_FILEDEF" info -IS_DECL<0 { - print -} - -IS_DECL==0 { - for (i=1; i < NF; i++) { - if (match($i, "^" OBJNAME "\\>" ) != 0) { - IS_DECL=i - break - } - } - - if (IS_DECL) { - for (i=1; i < IS_DECL; i++) { - if ($i != "const") { - DATATYPE=$i - } - } - print DATATYPE - print $IS_DECL - } - else { - print $0 - } -} - -IS_DECL > 0 && /;/ { - IS_DECL=-1 - print ";" -} diff --git a/cfecfs/eds2cfetbl/scripts/extract_typename.awk b/cfecfs/eds2cfetbl/scripts/extract_typename.awk new file mode 100644 index 0000000..339dfe7 --- /dev/null +++ b/cfecfs/eds2cfetbl/scripts/extract_typename.awk @@ -0,0 +1,22 @@ +#!/usr/bin/awk -f + +BEGIN { + RS="="; + ORS="\n"; + FS=" "; + TYPENAME=""; +} + +END { + if (TYPENAME == "") { + print "\nERROR: Unable to extract type name for table\n" > "/dev/stderr" + exit 1 + } +} + + +$NF ~ OBJNAME { + for (i=1;i +#include + +extern CFE_TBL_FileDef_t CFE_TBL_FileDef; + +int main(void) +{ + printf("%s\n", CFE_TBL_FileDef.ObjectName); + return 0; +} \ No newline at end of file diff --git a/cfecfs/eds2cfetbl/scripts/generate_eds_table_rules.cmake b/cfecfs/eds2cfetbl/scripts/generate_eds_table_rules.cmake index 0ca4d33..2edc025 100644 --- a/cfecfs/eds2cfetbl/scripts/generate_eds_table_rules.cmake +++ b/cfecfs/eds2cfetbl/scripts/generate_eds_table_rules.cmake @@ -28,6 +28,10 @@ set(TABLE_RULES) foreach(TBL_SRC ${SOURCES}) + if ($ENV{VERBOSE}) + message(STATUS "Running generate_eds_table_rules on ${TBL_SRC}") + endif() + if (TBL_SRC MATCHES "\\.c$") # Legacy C source files need a compile step before they can be used set(TABLE_LEGACY_FILE "${TMP_DIR}/${APP_NAME}_${TABLE_NAME}") diff --git a/cfecfs/eds2cfetbl/scripts/separate_filedef.awk b/cfecfs/eds2cfetbl/scripts/separate_filedef.awk new file mode 100644 index 0000000..0e89598 --- /dev/null +++ b/cfecfs/eds2cfetbl/scripts/separate_filedef.awk @@ -0,0 +1,24 @@ +#!/usr/bin/awk -f + +BEGIN { + RS="\n"; + ORS="\n"; + FS=" "; + IS_FILEDEF=0; # set nonzero at the start of the filedef +} + +IS_FILEDEF==0 { + if (/CFE_TBL_FileDef_t/) { + IS_FILEDEF=1 + } +} + +IS_FILEDEF==WANT_FILEDEF { + print +} + +IS_FILEDEF==1 { + if (/;/) { + IS_FILEDEF=0 + } +} diff --git a/cfecfs/eds2cfetbl/scripts/table_rule_template.d.in b/cfecfs/eds2cfetbl/scripts/table_rule_template.d.in index 4fa6648..e4c4db2 100644 --- a/cfecfs/eds2cfetbl/scripts/table_rule_template.d.in +++ b/cfecfs/eds2cfetbl/scripts/table_rule_template.d.in @@ -7,4 +7,5 @@ ${TABLE_BINARY}: CFE_TABLE_APPNAME := ${APP_NAME} ${TABLE_BINARY}: CFE_TABLE_BASENAME := ${TABLE_NAME} # Rules to build ${TABLE_BINARY} +${TABLE_BINARY}: $(TBLTOOL) ${TABLE_RULES} diff --git a/cfecfs/eds2cfetbl/scripts/table_wrapper.c.m4 b/cfecfs/eds2cfetbl/scripts/table_wrapper.c.m4 new file mode 100644 index 0000000..73d9001 --- /dev/null +++ b/cfecfs/eds2cfetbl/scripts/table_wrapper.c.m4 @@ -0,0 +1,7 @@ + +void EdsTableTool_GENERATOR(void) +{ +m4_include(TBLDEF_C) + + EdsTableTool_DoExport(&CFE_TBL_FileDef, &OBJNAME, sizeof(OBJNAME), "TYPENAME", sizeof(TYPENAME)); +} diff --git a/cfecfs/eds2cfetbl/scripts/tabletool_rule.mk b/cfecfs/eds2cfetbl/scripts/tabletool_rule.mk index 190eb05..14efece 100644 --- a/cfecfs/eds2cfetbl/scripts/tabletool_rule.mk +++ b/cfecfs/eds2cfetbl/scripts/tabletool_rule.mk @@ -1,6 +1,10 @@ # Makefile for EDS-based CFE table generation -C_INCLUDE_DIRS += $(MISSION_BINARY_DIR)/inc +# the basic set of flags to pass to the table tool +TBLTOOL_FLAGS += -e MISSION_DEFS=\"$(MISSION_DEFS)\" +TBLTOOL_FLAGS += -e CPUNAME=\"$(CFE_TABLE_CPUNAME)\" +TBLTOOL_FLAGS += -e APPNAME=\"$(CFE_TABLE_APPNAME)\" +TBLTOOL_FLAGS += -e TABLENAME=\"$(CFE_TABLE_BASENAME)\" .PHONY: cfetables @@ -15,4 +19,4 @@ cfetables: # As a workaround, $CURDIR is used. staging/%.tbl: @mkdir -pv "$(dir $(@))" - cd "$(dir $(@))" && $(TBLTOOL) $(TBLTOOL_FLAGS) "$(CURDIR)/$(<)" + $(TBLTOOL) -e 'OUTPUT_DIR="$(dir $(@))"' $(TBLTOOL_FLAGS) $(TBLTOOL_EXTRA_FLAGS) $(filter-out $(TBLTOOL),$(^)) diff --git a/cfecfs/edsmsg/CMakeLists.txt b/cfecfs/edsmsg/CMakeLists.txt index cae936f..bd003f9 100644 --- a/cfecfs/edsmsg/CMakeLists.txt +++ b/cfecfs/edsmsg/CMakeLists.txt @@ -14,17 +14,19 @@ ################################################################## # Module library -add_library(${DEP} STATIC - fsw/src/cfe_msg_init.c - fsw/src/cfe_msg_integrity.c - fsw/src/cfe_msg_msgid.c - fsw/src/cfe_msg_commonhdr.c - - fsw/src/cfe_msg_sechdr_cmd.c - fsw/src/cfe_msg_sechdr_tlm.c - - fsw/src/cfe_msg_dispatcher.c +add_library(edsmsg STATIC + fsw/src/edsmsg_init.c + fsw/src/edsmsg_integrity.c + fsw/src/edsmsg_msgid.c + fsw/src/edsmsg_commonhdr.c + fsw/src/edsmsg_sechdr_cmd.c + fsw/src/edsmsg_sechdr_tlm.c + fsw/src/edsmsg_dispatcher.c ) -target_include_directories(${DEP} PUBLIC fsw/inc) -target_link_libraries(${DEP} PRIVATE core_private) +target_include_directories(edsmsg PUBLIC fsw/inc) +target_link_libraries(edsmsg PRIVATE core_private) + +if (ENABLE_UNIT_TESTS) + add_subdirectory(fsw/ut-stubs) +endif() diff --git a/cfecfs/edsmsg/fsw/inc/edsmsg_dispatcher.h b/cfecfs/edsmsg/fsw/inc/edsmsg_dispatcher.h new file mode 100644 index 0000000..ee81450 --- /dev/null +++ b/cfecfs/edsmsg/fsw/inc/edsmsg_dispatcher.h @@ -0,0 +1,80 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** File : cfe_sb_eds.h +** +** Author : Joe Hickey +** +** Purpose: +** API for the Electronic Data Sheets (EDS) for software bus messages +** +** Contains prototypes for the generic pack, unpack, and dispatch routines +** that are part of the Software Bus (SB) application. These are the EDS +** API calls that actually work with instances of message objects, which +** are all some form of CCSDS_SpacePacket_t (the base type of all SB messages). +** +** This is a separate header file as these API calls are only available in +** an EDS-enabled build. +** +** Note - this public header file does not include any "EdsLib" headers directly... +** The SB EDS implementation abstracts the relevant EdsLib data types so that +** CFS applications are only dealing with the SB API directly, not going directly to +** EdsLib to perform data operations. This keeps the include paths simpler and +** eases migration. +** +*/ + +#ifndef EDSMSG_DISPATCHER_H +#define EDSMSG_DISPATCHER_H + +#include +#include +#include +#include + +/* It is an error to include this file in a non-EDS build (the function does not exist) */ +#ifndef CFE_EDS_ENABLED +#error "Cannot use EDS dispatcher in a non-EDS build" +#endif + +/** + * \brief Dispatch an incoming message to its handler function, given a dispatch table. + * + * The incoming message will be identified and matched against the given + * EDS-defined interface. + * + * If the message payload is defined by the application's EDS, the correct + * handler function in the dispatch table will be called. + * + * \note this function is generally not directly invoked from applications, it + * should be invoked through a wrapper generated from the EDS tool. The wrapper + * has a simpler API and ensures that the ID values passed in are correct. + * + * \param DeclIntfId The declared interface ID from EDS + * \param ComponentId The component ID from EDS + * \param Buffer Pointer to the Software Bus buffer (with headers) + * \param DispatchTable Pointer to the dispatch table + * \returns If successfully dispatched, returns code from handler function, otherwise an error code. + */ +CFE_Status_t CFE_EDSMSG_Dispatch(EdsLib_Id_t DeclIntfId, EdsLib_Id_t ComponentId, const CFE_SB_Buffer_t *Buffer, + const void *DispatchTable); + +#endif /* edsmsg_dispatcher_H */ diff --git a/cfecfs/edsmsg/fsw/inc/cfe_msg_hdr_eds.h b/cfecfs/edsmsg/fsw/inc/edsmsg_hdr.h similarity index 74% rename from cfecfs/edsmsg/fsw/inc/cfe_msg_hdr_eds.h rename to cfecfs/edsmsg/fsw/inc/edsmsg_hdr.h index 3013041..84a47d4 100644 --- a/cfecfs/edsmsg/fsw/inc/cfe_msg_hdr_eds.h +++ b/cfecfs/edsmsg/fsw/inc/edsmsg_hdr.h @@ -26,8 +26,8 @@ * - Used to construct message structures */ -#ifndef CFE_MSG_HDR_EDS_H -#define CFE_MSG_HDR_EDS_H +#ifndef EDSMSG_HDR_H +#define EDSMSG_HDR_H /* * Include Files @@ -71,7 +71,7 @@ .Message.CCSDS.CommonHdr = \ { \ .SecHdrFlags = (mid) >> 11, \ - .AppId = (mid)&0x7FF, \ + .AppId = (mid) & 0x7FF, \ .SeqFlag = 0x03, \ .Length = (size), \ }, \ @@ -83,20 +83,6 @@ * Type Definitions */ -/* - * The header size should be aligned to the largest possible type on the system. - * This is so conventional struct definitions will not add extra padding between - * the header and the payload. Specifically, there is still padding, but it will - * be reflected in sizeof(CFE_MSG_CommandHeader) as opposed simply existing as - * a gap between the header and payload. - */ -union CFE_EDSMSG_Align -{ - uintmax_t AlignInt; /**< alignment for largest native integer */ - void * AlignPtr; /**< alignment for pointers */ - double AlignDbl; /**< alignment for double-precision float */ -}; - /********************************************************************** * Structure definitions for full header * @@ -117,22 +103,6 @@ struct CFE_MSG_Message EdsDataType_CFE_HDR_Message_t BaseMsg; }; -/** - * \brief Aligned command header - * - * Df - */ -union CFE_EDSMSG_CommandHeader_Aligned -{ - /** - * EDS-defined command header structure - */ - EdsDataType_CFE_HDR_CommandHeader_t HeaderData; - - /* Member for alignment (unused in code) */ - union CFE_EDSMSG_Align Align; -}; - /** * \brief cFS command header * @@ -140,23 +110,7 @@ union CFE_EDSMSG_CommandHeader_Aligned */ struct CFE_MSG_CommandHeader { - union CFE_EDSMSG_CommandHeader_Aligned Content; -}; - -/** - * \brief cFS telemetry header - * - * This provides the definition of CFE_MSG_TelemetryHeader_t - */ -union CFE_EDSMSG_TelemetryHeader_Aligned -{ - /** - * EDS-defined telemetry header structure - */ - EdsDataType_CFE_HDR_TelemetryHeader_t HeaderData; - - /* Member for alignment (unused in code) */ - union CFE_EDSMSG_Align Align; + EdsDataType_CFE_HDR_CommandHeader_t HeaderData; }; /** @@ -169,7 +123,7 @@ struct CFE_MSG_TelemetryHeader /** * EDS-defined telemetry header structure */ - union CFE_EDSMSG_TelemetryHeader_Aligned Content; + EdsDataType_CFE_HDR_TelemetryHeader_t HeaderData; }; #endif /* CFE_MSG_HDR_EDS_H */ diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_dispatcher.c b/cfecfs/edsmsg/fsw/src/cfe_msg_dispatcher.c deleted file mode 100644 index e9acb87..0000000 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_dispatcher.c +++ /dev/null @@ -1,159 +0,0 @@ -/* -** GSC-18128-1, "Core Flight Executive Version 6.7" -** -** Copyright (c) 2006-2019 United States Government as represented by -** the Administrator of the National Aeronautics and Space Administration. -** All Rights Reserved. -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -/* -** Include Files -*/ -#include -#include - -#include "edslib_datatypedb.h" -#include "common_types.h" -#include "cfe_error.h" -#include "cfe_sb_api_typedefs.h" -#include "cfe_msg.h" -#include "cfe_config.h" - -#include "cfe_mission_eds_parameters.h" -#include "cfe_mission_eds_interface_parameters.h" -#include "cfe_missionlib_api.h" -#include "cfe_missionlib_runtime.h" - -CFE_Status_t CFE_MSG_EdsDispatch(uint16 InterfaceID, uint16 IndicationIndex, uint16 DispatchTableID, - const CFE_SB_Buffer_t *Buffer, const void *DispatchTable) -{ - const EdsLib_DatabaseObject_t * GD; - CFE_MissionLib_TopicInfo_t TopicInfo; - CFE_MissionLib_IndicationInfo_t IndicationInfo; - EdsLib_DataTypeDB_TypeInfo_t TypeInfo; - EdsInterface_CFE_SB_SoftwareBus_PubSub_t PubSubParams; - EdsLib_Id_t ArgumentType; - int32_t Status; - uint16_t TopicId; - uint16_t DispatchOffset; - CFE_MSG_Size_t BufferSize; - union - { - cpuaddr MemAddr; - const void *GenericPtr; - int32 (**DispatchFunc)(const CFE_SB_Buffer_t *); - } HandlerPtr; - - GD = CFE_Config_GetObjPointer(CFE_CONFIGID_MISSION_EDS_DB); - - CFE_MSG_GetSize(&Buffer->Msg, &BufferSize); - - HandlerPtr.GenericPtr = DispatchTable; - DispatchOffset = 0; - CFE_MissionLib_Get_PubSub_Parameters(&PubSubParams, &Buffer->Msg.BaseMsg); - - switch (InterfaceID) - { - case EDS_INTERFACE_ID(CFE_SB_Telemetry): - { - EdsComponent_CFE_SB_Publisher_t PublisherParams; - CFE_MissionLib_UnmapPublisherComponent(&PublisherParams, &PubSubParams); - TopicId = PublisherParams.Telemetry.TopicId; - break; - } - case EDS_INTERFACE_ID(CFE_SB_Telecommand): - { - EdsComponent_CFE_SB_Listener_t ListenerParams; - CFE_MissionLib_UnmapListenerComponent(&ListenerParams, &PubSubParams); - TopicId = ListenerParams.Telecommand.TopicId; - break; - } - default: - TopicId = 0; - break; - } - - Status = CFE_MissionLib_GetTopicInfo(&CFE_SOFTWAREBUS_INTERFACE, InterfaceID, TopicId, &TopicInfo); - if (Status != CFE_MISSIONLIB_SUCCESS) - { - return CFE_STATUS_UNKNOWN_MSG_ID; - } - - if (TopicInfo.DispatchTableId != DispatchTableID) - { - return CFE_STATUS_EXTERNAL_RESOURCE_FAIL; - } - - Status = CFE_MissionLib_GetIndicationInfo(&CFE_SOFTWAREBUS_INTERFACE, InterfaceID, TopicId, IndicationIndex, - &IndicationInfo); - if (Status != CFE_MISSIONLIB_SUCCESS || IndicationInfo.NumArguments != 1) - { - /* - * This dispatch function only handles single-argument commands defined in EDS. - * This is really a programmer/EDS error and not a runtime error if this occurs - */ - return CFE_SB_NOT_IMPLEMENTED; - } - - Status = CFE_MissionLib_GetArgumentType(&CFE_SOFTWAREBUS_INTERFACE, InterfaceID, TopicId, IndicationIndex, 1, - &ArgumentType); - if (Status != CFE_MISSIONLIB_SUCCESS) - { - return CFE_SB_INTERNAL_ERR; - } - - if (IndicationInfo.SubcommandArgumentId == 1 && IndicationInfo.NumSubcommands > 0) - { - /* Derived command case -- the indication corresponds to a multiple entries in the dispatch table. - * The actual type of the argument must be determined to figure out which one to invoke. */ - EdsLib_DataTypeDB_DerivativeObjectInfo_t DerivObjInfo; - - Status = EdsLib_DataTypeDB_IdentifyBuffer(GD, ArgumentType, Buffer, &DerivObjInfo); - if (Status != EDSLIB_SUCCESS) - { - return CFE_STATUS_UNKNOWN_MSG_ID; - } - - /* NOTE: The "IdentifyBuffer" outputs a 0-based index, but the Subcommand is a 1-based index */ - Status = CFE_MissionLib_GetSubcommandOffset(&CFE_SOFTWAREBUS_INTERFACE, InterfaceID, TopicId, IndicationIndex, - 1 + DerivObjInfo.DerivativeTableIndex, &DispatchOffset); - if (Status != EDSLIB_SUCCESS) - { - return CFE_STATUS_BAD_COMMAND_CODE; - } - - ArgumentType = DerivObjInfo.EdsId; - } - - Status = EdsLib_DataTypeDB_GetTypeInfo(GD, ArgumentType, &TypeInfo); - if (Status != EDSLIB_SUCCESS) - { - return CFE_SB_INTERNAL_ERR; - } - - if (TypeInfo.Size.Bytes > BufferSize) - { - return CFE_STATUS_WRONG_MSG_LENGTH; - } - - HandlerPtr.MemAddr += TopicInfo.DispatchStartOffset; - HandlerPtr.MemAddr += DispatchOffset; - if (*HandlerPtr.DispatchFunc == NULL) - { - return CFE_STATUS_EXTERNAL_RESOURCE_FAIL; - } - - return (*HandlerPtr.DispatchFunc)(Buffer); -} diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_commonhdr.c b/cfecfs/edsmsg/fsw/src/edsmsg_commonhdr.c similarity index 87% rename from cfecfs/edsmsg/fsw/src/cfe_msg_commonhdr.c rename to cfecfs/edsmsg/fsw/src/edsmsg_commonhdr.c index 8a5d3dd..5776e52 100644 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_commonhdr.c +++ b/cfecfs/edsmsg/fsw/src/edsmsg_commonhdr.c @@ -31,10 +31,10 @@ #include "ccsds_spacepacket_eds_datatypes.h" #include "cfe_hdr_eds_datatypes.h" -#define CFE_MSG_SHDR_PRESENT_MASK_BIT (CCSDS_SecHdrFlags_Tlm & CCSDS_SecHdrFlags_Cmd) -#define CFE_MSG_SHDR_TYPE_MASK_BIT (CCSDS_SecHdrFlags_Tlm ^ CCSDS_SecHdrFlags_Cmd) -#define CFE_MSG_SHDR_TYPE_TLM_BIT (CCSDS_SecHdrFlags_BareTlm & CCSDS_SecHdrFlags_Tlm) -#define CFE_MSG_SHDR_TYPE_CMD_BIT (CCSDS_SecHdrFlags_BareCmd & CCSDS_SecHdrFlags_Cmd) +#define CFE_MSG_SHDR_PRESENT_MASK_BIT (EdsLabel_CCSDS_SecHdrFlags_Tlm & EdsLabel_CCSDS_SecHdrFlags_Cmd) +#define CFE_MSG_SHDR_TYPE_MASK_BIT (EdsLabel_CCSDS_SecHdrFlags_Tlm ^ EdsLabel_CCSDS_SecHdrFlags_Cmd) +#define CFE_MSG_SHDR_TYPE_TLM_BIT (EdsLabel_CCSDS_SecHdrFlags_BareTlm & EdsLabel_CCSDS_SecHdrFlags_Tlm) +#define CFE_MSG_SHDR_TYPE_CMD_BIT (EdsLabel_CCSDS_SecHdrFlags_BareCmd & EdsLabel_CCSDS_SecHdrFlags_Cmd) /*---------------------------------------------------------------- * @@ -53,7 +53,7 @@ CFE_Status_t CFE_MSG_GetHeaderVersion(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_H return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; *Version = Hdr->VersionId; @@ -77,7 +77,7 @@ CFE_Status_t CFE_MSG_SetHeaderVersion(CFE_MSG_Message_t *MsgPtr, CFE_MSG_HeaderV return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; Hdr->VersionId = Version; @@ -101,16 +101,16 @@ CFE_Status_t CFE_MSG_GetType(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t *Ty return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; switch (Hdr->SecHdrFlags) { - case CCSDS_SecHdrFlags_BareTlm: - case CCSDS_SecHdrFlags_Tlm: + case EdsLabel_CCSDS_SecHdrFlags_BareTlm: + case EdsLabel_CCSDS_SecHdrFlags_Tlm: *Type = CFE_MSG_Type_Tlm; break; - case CCSDS_SecHdrFlags_BareCmd: - case CCSDS_SecHdrFlags_Cmd: + case EdsLabel_CCSDS_SecHdrFlags_BareCmd: + case EdsLabel_CCSDS_SecHdrFlags_Cmd: *Type = CFE_MSG_Type_Cmd; break; default: @@ -131,7 +131,7 @@ CFE_Status_t CFE_MSG_GetType(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t *Ty *-----------------------------------------------------------------*/ CFE_Status_t CFE_MSG_SetType(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t Type) { - EdsDataType_CCSDS_CommonHdr_t * Hdr; + EdsDataType_CCSDS_CommonHdr_t *Hdr; EdsDataType_CCSDS_SecHdrFlags_t SetVal; if (MsgPtr == NULL) @@ -139,7 +139,7 @@ CFE_Status_t CFE_MSG_SetType(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Type_t Type) return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; SetVal = 0; if (Type == CFE_MSG_Type_Tlm) @@ -178,7 +178,7 @@ CFE_Status_t CFE_MSG_GetHasSecondaryHeader(const CFE_MSG_Message_t *MsgPtr, bool return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; *HasSecondary = (Hdr->SecHdrFlags & CFE_MSG_SHDR_PRESENT_MASK_BIT) != 0; @@ -196,14 +196,14 @@ CFE_Status_t CFE_MSG_GetHasSecondaryHeader(const CFE_MSG_Message_t *MsgPtr, bool CFE_Status_t CFE_MSG_SetHasSecondaryHeader(CFE_MSG_Message_t *MsgPtr, bool HasSecondary) { EdsDataType_CCSDS_SecHdrFlags_t SetVal; - EdsDataType_CCSDS_CommonHdr_t * Hdr; + EdsDataType_CCSDS_CommonHdr_t *Hdr; if (MsgPtr == NULL) { return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; if (HasSecondary) { @@ -237,7 +237,7 @@ CFE_Status_t CFE_MSG_GetApId(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_ApId_t *Ap return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; *ApId = Hdr->AppId; @@ -261,7 +261,7 @@ CFE_Status_t CFE_MSG_SetApId(CFE_MSG_Message_t *MsgPtr, CFE_MSG_ApId_t ApId) return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; Hdr->AppId = ApId; @@ -285,7 +285,7 @@ CFE_Status_t CFE_MSG_GetSegmentationFlag(const CFE_MSG_Message_t *MsgPtr, CFE_MS return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* JPHFIX: should be EDS enum? */ switch (Hdr->SeqFlag) @@ -327,7 +327,7 @@ CFE_Status_t CFE_MSG_SetSegmentationFlag(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Segm return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; /* JPHFIX: should be EDS enum? */ switch (SegFlag) @@ -368,7 +368,7 @@ CFE_Status_t CFE_MSG_GetSequenceCount(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_S return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; *SeqCnt = Hdr->Sequence; @@ -392,7 +392,7 @@ CFE_Status_t CFE_MSG_SetSequenceCount(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Sequenc return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; Hdr->Sequence = SeqCnt; @@ -429,7 +429,7 @@ CFE_Status_t CFE_MSG_GetSize(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_Size_t *Si return CFE_MSG_BAD_ARGUMENT; } - Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* note that EDS pack/unpack reapplies the CCSDS length calibration */ *Size = Hdr->Length + 7; @@ -459,7 +459,7 @@ CFE_Status_t CFE_MSG_SetSize(CFE_MSG_Message_t *MsgPtr, CFE_MSG_Size_t Size) return CFE_MSG_BAD_ARGUMENT; } - Hdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + Hdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; Hdr->Length = Size - 7; diff --git a/cfecfs/edsmsg/fsw/src/edsmsg_dispatcher.c b/cfecfs/edsmsg/fsw/src/edsmsg_dispatcher.c new file mode 100644 index 0000000..a0586ab --- /dev/null +++ b/cfecfs/edsmsg/fsw/src/edsmsg_dispatcher.c @@ -0,0 +1,351 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** Include Files +*/ +#include +#include + +#include "edslib_datatypedb.h" +#include "edslib_intfdb.h" +#include "common_types.h" +#include "cfe_error.h" +#include "cfe_sb_api_typedefs.h" +#include "cfe_msg.h" +#include "edsmsg_dispatcher.h" +#include "cfe_config.h" + +#include "cfe_mission_eds_parameters.h" +#include "cfe_mission_eds_interface_parameters.h" +#include "cfe_missionlib_api.h" +#include "cfe_missionlib_runtime.h" + +/* + * A generic typedef for a dispatch function implementation + * Note the actual implementation is going to have a more specific data type + * for the argument, but it will always be a single const pointer. + */ +typedef int32 (*CFE_EDSMSG_DispatchFunc_t)(const CFE_SB_Buffer_t *); + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds topicinfo for a command message + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_FindCmdTopicInfo(const EdsInterface_CFE_SB_SoftwareBus_PubSub_t *PubSubParams, + CFE_MissionLib_TopicInfo_t *TopicInfo) +{ + EdsComponent_CFE_SB_Listener_t ListenerParams; + int32_t Status; + + CFE_MissionLib_UnmapListenerComponent(&ListenerParams, PubSubParams); + + Status = CFE_MissionLib_GetTopicInfo(&CFE_SOFTWAREBUS_INTERFACE, ListenerParams.Telecommand.TopicId, TopicInfo); + if (Status != CFE_MISSIONLIB_SUCCESS) + { + return CFE_STATUS_UNKNOWN_MSG_ID; + } + else + { + return CFE_SUCCESS; + } +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds topicinfo for a telemetry message + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_FindTlmTopicInfo(const EdsInterface_CFE_SB_SoftwareBus_PubSub_t *PubSubParams, + CFE_MissionLib_TopicInfo_t *TopicInfo) +{ + EdsComponent_CFE_SB_Publisher_t PublisherParams; + int32_t Status; + + CFE_MissionLib_UnmapPublisherComponent(&PublisherParams, PubSubParams); + + Status = CFE_MissionLib_GetTopicInfo(&CFE_SOFTWAREBUS_INTERFACE, PublisherParams.Telemetry.TopicId, TopicInfo); + if (Status != CFE_MISSIONLIB_SUCCESS) + { + return CFE_STATUS_UNKNOWN_MSG_ID; + } + else + { + return CFE_SUCCESS; + } +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds topicinfo for a message. First find the type of message (TLM or CMD) + * and then call the apprpriate sub-function to get the topic info + * + * If this fails, zero-fill the output because topicID 0 is never valid + * + *-----------------------------------------------------------------*/ +void CFE_EDSMSG_Dispatch_GetTopicInfo(const CFE_SB_Buffer_t *Buffer, CFE_MissionLib_TopicInfo_t *TopicInfo) +{ + EdsInterface_CFE_SB_SoftwareBus_PubSub_t PubSubParams; + const EdsDataType_CFE_HDR_Message_t *Hdr; + + Hdr = (const EdsDataType_CFE_HDR_Message_t *)(const void *)&Buffer->Msg; + + CFE_MissionLib_Get_PubSub_Parameters(&PubSubParams, Hdr); + + /* In general FSW will call this only on "Listener" interfaces (e.g. SB pipes where + * cmds come in) as opposed to publisher interfaces (e.g. telemetry output), however + * it is still theoretically possible to dispatch TLM */ + if (CFE_MissionLib_PubSub_IsListenerComponent(&PubSubParams)) + { + CFE_EDSMSG_FindCmdTopicInfo(&PubSubParams, TopicInfo); + } + else if (CFE_MissionLib_PubSub_IsPublisherComponent(&PubSubParams)) + { + CFE_EDSMSG_FindTlmTopicInfo(&PubSubParams, TopicInfo); + } + else + { + memset(&TopicInfo, 0, sizeof(TopicInfo)); + } +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Confirms that the topic matches the expected component interface + * + *-----------------------------------------------------------------*/ +bool CFE_EDSMSG_Dispatch_CheckComponentMatch(EdsLib_Id_t ComponentId, CFE_MissionLib_TopicInfo_t *TopicInfo) +{ + const EdsLib_DatabaseObject_t *GD; + EdsLib_IntfDB_InterfaceInfo_t IntfInfo; + int32_t Status; + + GD = CFE_Config_GetObjPointer(CFE_CONFIGID_MISSION_EDS_DB); + + Status = EdsLib_IntfDB_GetComponentInterfaceInfo(GD, TopicInfo->ParentIntfId, &IntfInfo); + if (Status != EDSLIB_SUCCESS) + { + return false; + } + + /* The interfaces must match - if they do not match it means this dispatch table cannot be used */ + return (EdsLib_Is_Similar(ComponentId, IntfInfo.ParentCompEdsId)); +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds the EdsId for the argument (message data type) + * This just finds the base type using the information in the EDS DB only + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_Dispatch_FindArgBaseType(EdsLib_Id_t DeclIntfId, EdsLib_Id_t CompIntfId, + EdsLib_Id_t *ArgTypeBuf) +{ + const EdsLib_DatabaseObject_t *GD; + EdsLib_Id_t CommandEdsId; + int32_t Status; + + GD = CFE_Config_GetObjPointer(CFE_CONFIGID_MISSION_EDS_DB); + + /* The SB interfaces are split between "publisher" and "listener" such that + * there is expected to be just a singluar command associated with the I/F */ + Status = EdsLib_IntfDB_FindAllCommands(GD, DeclIntfId, &CommandEdsId, 1); + if (Status != EDSLIB_SUCCESS) + { + return CFE_STATUS_VALIDATION_FAILURE; + } + + /* Likewise, the single command should have a singular argument */ + Status = EdsLib_IntfDB_FindAllArgumentTypes(GD, CommandEdsId, CompIntfId, ArgTypeBuf, 1); + if (Status != EDSLIB_SUCCESS) + { + return CFE_STATUS_VALIDATION_FAILURE; + } + + return CFE_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds the EdsId for the argument (message data type) + * + * This uses the constraints and values within the message (e.g. command code) + * to identify a derived data type. This is the final identification step and + * once the type is fully identified, the size of the message should match its + * expected/defined size in EDS + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_Dispatch_CheckActualBufferType(const CFE_SB_Buffer_t *Buffer, EdsLib_Id_t *EdsId, + uint32_t *DispatchTblPosition) +{ + const EdsLib_DatabaseObject_t *GD; + EdsLib_DataTypeDB_TypeInfo_t TypeInfo; + EdsLib_DataTypeDB_DerivativeObjectInfo_t DerivObjInfo; + int32_t Status; + CFE_MSG_Size_t MessageSize; + CFE_Status_t ReturnCode; + + GD = CFE_Config_GetObjPointer(CFE_CONFIGID_MISSION_EDS_DB); + + CFE_MSG_GetSize(&Buffer->Msg, &MessageSize); + + /* Check if the argument type has derivatives. This is typical for CMD interfaces where there + * are many possible command codes, and in this case each command code will have its own + * entry in the dispatch table. If this fails, it does not fail the overall process, it just + * means that the argument is not derived. */ + Status = EdsLib_DataTypeDB_IdentifyBufferWithSize(GD, *EdsId, Buffer, MessageSize, &DerivObjInfo); + if (Status == EDSLIB_SUCCESS) + { + *EdsId = DerivObjInfo.EdsId; + *DispatchTblPosition = DerivObjInfo.DerivativeTableIndex; + } + else + { + /* Non derived, there is just one entry, it is always first */ + *DispatchTblPosition = 0; + } + + /* This lookup should not fail */ + Status = EdsLib_DataTypeDB_GetTypeInfo(GD, *EdsId, &TypeInfo); + if (Status != EDSLIB_SUCCESS) + { + ReturnCode = CFE_SB_INTERNAL_ERR; + } + else if (TypeInfo.Size.Bytes != MessageSize) + { + ReturnCode = CFE_STATUS_WRONG_MSG_LENGTH; + } + else + { + /* Everything checked out */ + ReturnCode = CFE_SUCCESS; + } + + return ReturnCode; +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Completely look up the data type from the supplied interface IDs + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_Dispatch_FindArgType(EdsLib_Id_t DeclIntfId, EdsLib_Id_t ParentIntfId, + const CFE_SB_Buffer_t *Buffer, uint32_t *DispatchPosition) +{ + CFE_Status_t ReturnCode; + EdsLib_Id_t ArgType; + + *DispatchPosition = 0; + + /* First lookup is to simply identify the base type for this intf */ + /* This is independent of the message content, strictly based on the I/F */ + ReturnCode = CFE_EDSMSG_Dispatch_FindArgBaseType(DeclIntfId, ParentIntfId, &ArgType); + if (ReturnCode == CFE_SUCCESS) + { + /* Second lookup is to check if its a derived type, and also confirm the size matches. + * This requires looking at fields within the actual msg */ + ReturnCode = CFE_EDSMSG_Dispatch_CheckActualBufferType(Buffer, &ArgType, DispatchPosition); + } + + return ReturnCode; +} + +/*---------------------------------------------------------------- + * + * Local helper function + * Finds the matching handler function from the dispatch table + * + *-----------------------------------------------------------------*/ +CFE_EDSMSG_DispatchFunc_t CFE_EDSMSG_Dispatch_LookupHandler(const void *DispatchTable, size_t DispatchStartOffset, + uint32_t DispatchPosition) +{ + const uint8 *MemAddr; + const CFE_EDSMSG_DispatchFunc_t *DispatchTbl; + + /* calculate the address of the first handler routine for this intf + * Note the START offsets are in bytes, so this must be done in uint8* logic */ + MemAddr = (const uint8 *)DispatchTable; + MemAddr += DispatchStartOffset; + + /* now shift to the correct type (function pointer) and apply the position offset */ + DispatchTbl = (const CFE_EDSMSG_DispatchFunc_t *)MemAddr; + + return (DispatchTbl[DispatchPosition]); +} + +/*---------------------------------------------------------------- + * + * Public API call + * Dispatch the message based on the IDs and dispatch table + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_EDSMSG_Dispatch(EdsLib_Id_t DeclIntfId, EdsLib_Id_t ComponentId, const CFE_SB_Buffer_t *Buffer, + const void *DispatchTable) +{ + CFE_Status_t ReturnCode; + CFE_MissionLib_TopicInfo_t TopicInfo; + uint32_t DispatchPosition; + CFE_EDSMSG_DispatchFunc_t Handler; + + DispatchPosition = 0; + Handler = NULL; + CFE_EDSMSG_Dispatch_GetTopicInfo(Buffer, &TopicInfo); + + /* Check that the dispatch table matches this buffer (they map to the same EDS component) + * Otherwise it means there is a mismatch between the message and the + * dispatch table type and this message cannot be dispatched using this table */ + if (CFE_EDSMSG_Dispatch_CheckComponentMatch(ComponentId, &TopicInfo)) + { + ReturnCode = CFE_EDSMSG_Dispatch_FindArgType(DeclIntfId, TopicInfo.ParentIntfId, Buffer, &DispatchPosition); + } + else + { + ReturnCode = CFE_STATUS_VALIDATION_FAILURE; + } + + if (ReturnCode == CFE_SUCCESS) + { + Handler = CFE_EDSMSG_Dispatch_LookupHandler(DispatchTable, TopicInfo.DispatchStartOffset, DispatchPosition); + } + + if (ReturnCode == CFE_SUCCESS) + { + /* Now actually call the handler, if its defined */ + if (Handler != NULL) + { + ReturnCode = Handler(Buffer); + } + else + { + /* this means there was no entry in the table for this cmd */ + ReturnCode = CFE_STATUS_NOT_IMPLEMENTED; + } + } + + return ReturnCode; +} diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_init.c b/cfecfs/edsmsg/fsw/src/edsmsg_init.c similarity index 96% rename from cfecfs/edsmsg/fsw/src/cfe_msg_init.c rename to cfecfs/edsmsg/fsw/src/edsmsg_init.c index 7df8011..5c1a76a 100644 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_init.c +++ b/cfecfs/edsmsg/fsw/src/edsmsg_init.c @@ -56,7 +56,7 @@ CFE_Status_t CFE_MSG_Init(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId, CFE_M Status = CFE_MSG_SetMsgId(MsgPtr, MsgId); if (Status == CFE_SUCCESS) { - CommonHdr = (EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + CommonHdr = (EdsDataType_CCSDS_CommonHdr_t *)(void *)MsgPtr; /* Default to complete packets */ CommonHdr->SeqFlag = 3; /* jphfix: enum? */ diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_integrity.c b/cfecfs/edsmsg/fsw/src/edsmsg_integrity.c similarity index 100% rename from cfecfs/edsmsg/fsw/src/cfe_msg_integrity.c rename to cfecfs/edsmsg/fsw/src/edsmsg_integrity.c diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_msgid.c b/cfecfs/edsmsg/fsw/src/edsmsg_msgid.c similarity index 85% rename from cfecfs/edsmsg/fsw/src/cfe_msg_msgid.c rename to cfecfs/edsmsg/fsw/src/edsmsg_msgid.c index 5222332..aef3664 100644 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_msgid.c +++ b/cfecfs/edsmsg/fsw/src/edsmsg_msgid.c @@ -50,7 +50,7 @@ CFE_Status_t CFE_MSG_GetTypeFromMsgId(CFE_SB_MsgId_t MsgId, CFE_MSG_Type_t *Type } else { - Params.MsgId = MsgId; + Params.MsgId.Value = CFE_SB_MsgIdToValue(MsgId); if (CFE_MissionLib_PubSub_IsListenerComponent(&Params)) { *Type = CFE_MSG_Type_Cmd; @@ -74,15 +74,17 @@ CFE_Status_t CFE_MSG_GetTypeFromMsgId(CFE_SB_MsgId_t MsgId, CFE_MSG_Type_t *Type CFE_Status_t CFE_MSG_GetMsgId(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *MsgId) { EdsInterface_CFE_SB_SoftwareBus_PubSub_t Params; + const EdsDataType_CFE_HDR_Message_t *Hdr; if (MsgPtr == NULL || MsgId == NULL) { return CFE_MSG_BAD_ARGUMENT; } - CFE_MissionLib_Get_PubSub_Parameters(&Params, &MsgPtr->BaseMsg); + Hdr = (const EdsDataType_CFE_HDR_Message_t *)(const void *)MsgPtr; + CFE_MissionLib_Get_PubSub_Parameters(&Params, Hdr); - *MsgId = Params.MsgId; + *MsgId = CFE_SB_ValueToMsgId(Params.MsgId.Value); return CFE_SUCCESS; } @@ -90,6 +92,7 @@ CFE_Status_t CFE_MSG_GetMsgId(const CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t *M CFE_Status_t CFE_MSG_SetMsgId(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId) { EdsInterface_CFE_SB_SoftwareBus_PubSub_t Params; + EdsDataType_CFE_HDR_Message_t *Hdr; /* * NOTE: in reality the EDS may map any bits of the MsgId to the message, there is @@ -101,8 +104,9 @@ CFE_Status_t CFE_MSG_SetMsgId(CFE_MSG_Message_t *MsgPtr, CFE_SB_MsgId_t MsgId) return CFE_MSG_BAD_ARGUMENT; } - Params.MsgId = MsgId; - CFE_MissionLib_Set_PubSub_Parameters(&MsgPtr->BaseMsg, &Params); + Hdr = (EdsDataType_CFE_HDR_Message_t *)(void *)MsgPtr; + Params.MsgId.Value = CFE_SB_MsgIdToValue(MsgId); + CFE_MissionLib_Set_PubSub_Parameters(Hdr, &Params); return CFE_SUCCESS; } diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_cmd.c b/cfecfs/edsmsg/fsw/src/edsmsg_sechdr_cmd.c similarity index 91% rename from cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_cmd.c rename to cfecfs/edsmsg/fsw/src/edsmsg_sechdr_cmd.c index 0cd992a..66f4fb7 100644 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_cmd.c +++ b/cfecfs/edsmsg/fsw/src/edsmsg_sechdr_cmd.c @@ -37,17 +37,17 @@ CFE_Status_t CFE_MSG_GetFcnCode(const CFE_MSG_Message_t *MsgPtr, CFE_MSG_FcnCode_t *FcnCode) { const EdsDataType_CFE_HDR_CommandHeader_t *CmdPtr; - const EdsDataType_CCSDS_CommonHdr_t * CommonHdr; + const EdsDataType_CCSDS_CommonHdr_t *CommonHdr; if (MsgPtr == NULL || FcnCode == NULL) { return CFE_MSG_BAD_ARGUMENT; } - CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* This confirms if it is OK to access the pointer as a "CommandHeader" type */ - if (CommonHdr->SecHdrFlags != CCSDS_SecHdrFlags_Cmd) + if (CommonHdr->SecHdrFlags != EdsLabel_CCSDS_SecHdrFlags_Cmd) { return CFE_MSG_WRONG_MSG_TYPE; } @@ -77,10 +77,10 @@ CFE_Status_t CFE_MSG_SetFcnCode(CFE_MSG_Message_t *MsgPtr, CFE_MSG_FcnCode_t Fcn return CFE_MSG_BAD_ARGUMENT; } - CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* This confirms if it is OK to access the pointer as a "CommandHeader" type */ - if (CommonHdr->SecHdrFlags != CCSDS_SecHdrFlags_Cmd) + if (CommonHdr->SecHdrFlags != EdsLabel_CCSDS_SecHdrFlags_Cmd) { return CFE_MSG_WRONG_MSG_TYPE; } diff --git a/cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_tlm.c b/cfecfs/edsmsg/fsw/src/edsmsg_sechdr_tlm.c similarity index 88% rename from cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_tlm.c rename to cfecfs/edsmsg/fsw/src/edsmsg_sechdr_tlm.c index cfdae19..9cdb894 100644 --- a/cfecfs/edsmsg/fsw/src/cfe_msg_sechdr_tlm.c +++ b/cfecfs/edsmsg/fsw/src/edsmsg_sechdr_tlm.c @@ -35,17 +35,17 @@ CFE_Status_t CFE_MSG_SetMsgTime(CFE_MSG_Message_t *MsgPtr, CFE_TIME_SysTime_t NewTime) { EdsDataType_CFE_HDR_TelemetryHeader_t *TlmPtr; - const EdsDataType_CCSDS_CommonHdr_t * CommonHdr; + const EdsDataType_CCSDS_CommonHdr_t *CommonHdr; if (MsgPtr == NULL) { return CFE_MSG_BAD_ARGUMENT; } - CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* This confirms if it is OK to access the pointer as a "TelemetryHeader" type */ - if (CommonHdr->SecHdrFlags != CCSDS_SecHdrFlags_Tlm) + if (CommonHdr->SecHdrFlags != EdsLabel_CCSDS_SecHdrFlags_Tlm) { return CFE_MSG_WRONG_MSG_TYPE; } @@ -78,17 +78,17 @@ CFE_Status_t CFE_MSG_SetMsgTime(CFE_MSG_Message_t *MsgPtr, CFE_TIME_SysTime_t Ne CFE_Status_t CFE_MSG_GetMsgTime(const CFE_MSG_Message_t *MsgPtr, CFE_TIME_SysTime_t *Time) { const EdsDataType_CFE_HDR_TelemetryHeader_t *TlmPtr; - const EdsDataType_CCSDS_CommonHdr_t * CommonHdr; + const EdsDataType_CCSDS_CommonHdr_t *CommonHdr; if (MsgPtr == NULL || Time == NULL) { return CFE_MSG_BAD_ARGUMENT; } - CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)MsgPtr; + CommonHdr = (const EdsDataType_CCSDS_CommonHdr_t *)(const void *)MsgPtr; /* This confirms if it is OK to access the pointer as a "TelemetryHeader" type */ - if (CommonHdr->SecHdrFlags != CCSDS_SecHdrFlags_Tlm) + if (CommonHdr->SecHdrFlags != EdsLabel_CCSDS_SecHdrFlags_Tlm) { return CFE_MSG_WRONG_MSG_TYPE; } diff --git a/cfecfs/edsmsg/fsw/ut-stubs/CMakeLists.txt b/cfecfs/edsmsg/fsw/ut-stubs/CMakeLists.txt new file mode 100644 index 0000000..965554e --- /dev/null +++ b/cfecfs/edsmsg/fsw/ut-stubs/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation +# +# Copyright (c) 2020 United States Government as represented by +# the Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Build script for EDS message handler module unit test stubs +# The EDS version has a dispatcher function that is not in the normal core API +add_library(ut_edsmsg_stubs + edsmsg_dispatcher_stubs.c +) + +target_include_directories(ut_edsmsg_stubs PUBLIC + $ + $ +) + +target_link_libraries(ut_edsmsg_stubs + ut_assert +) diff --git a/cfecfs/edsmsg/fsw/ut-stubs/edsmsg_dispatcher_stubs.c b/cfecfs/edsmsg/fsw/ut-stubs/edsmsg_dispatcher_stubs.c new file mode 100644 index 0000000..7d33a5a --- /dev/null +++ b/cfecfs/edsmsg/fsw/ut-stubs/edsmsg_dispatcher_stubs.c @@ -0,0 +1,48 @@ +/* +** GSC-18128-1, "Core Flight Executive Version 6.7" +** +** Copyright (c) 2006-2019 United States Government as represented by +** the Administrator of the National Aeronautics and Space Administration. +** All Rights Reserved. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/** + * @file + * + * Auto-Generated stub implementations for functions defined in edsmsg_dispatcher header + */ + +#include "edsmsg_dispatcher.h" +#include "utgenstub.h" + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_EDSMSG_Dispatch() + * ---------------------------------------------------- + */ +CFE_Status_t CFE_EDSMSG_Dispatch(EdsLib_Id_t DeclIntfId, EdsLib_Id_t ComponentId, const CFE_SB_Buffer_t *Buffer, + const void *DispatchTable) +{ + UT_GenStub_SetupReturnBuffer(CFE_EDSMSG_Dispatch, CFE_Status_t); + + UT_GenStub_AddParam(CFE_EDSMSG_Dispatch, EdsLib_Id_t, DeclIntfId); + UT_GenStub_AddParam(CFE_EDSMSG_Dispatch, EdsLib_Id_t, ComponentId); + UT_GenStub_AddParam(CFE_EDSMSG_Dispatch, const CFE_SB_Buffer_t *, Buffer); + UT_GenStub_AddParam(CFE_EDSMSG_Dispatch, const void *, DispatchTable); + + UT_GenStub_Execute(CFE_EDSMSG_Dispatch, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_EDSMSG_Dispatch, CFE_Status_t); +} diff --git a/cfecfs/edsmsg/mission_build.cmake b/cfecfs/edsmsg/mission_build.cmake index fd50bbb..e5e6aef 100644 --- a/cfecfs/edsmsg/mission_build.cmake +++ b/cfecfs/edsmsg/mission_build.cmake @@ -19,6 +19,6 @@ # Generate the header definition files, use local default for this module) generate_config_includefile( FILE_NAME "cfe_msg_hdr.h" - FALLBACK_FILE "${CMAKE_CURRENT_LIST_DIR}/fsw/inc/cfe_msg_hdr_eds.h" + FALLBACK_FILE "${CMAKE_CURRENT_LIST_DIR}/fsw/inc/edsmsg_hdr.h" ) diff --git a/cfecfs/missionlib/CMakeLists.txt b/cfecfs/missionlib/CMakeLists.txt index 48c0def..e6f921d 100644 --- a/cfecfs/missionlib/CMakeLists.txt +++ b/cfecfs/missionlib/CMakeLists.txt @@ -38,27 +38,11 @@ # subdirectory. The contents of this directory should be included on the sedstool command # line which is handled outside of this script. # -cmake_minimum_required(VERSION 3.5) project(EDS_CFECFS_MISSIONLIB C) # When the EDS toolchain executes it produces a number of files based on the mission name -# By convention, the filenames will be all lowercase, and global symbols within those files -# will be all UPPERCASE. The mission name is used to generate a unique prefix. string(TOLOWER ${MISSION_NAME}_eds EDS_FILE_PREFIX) -string(TOUPPER ${MISSION_NAME} EDS_SYMBOL_PREFIX) -file(RELATIVE_PATH ARCH_BINARY_SUBDIR ${MISSION_BINARY_DIR} ${CMAKE_BINARY_DIR}) -if ("${ARCH_BINARY_SUBDIR}" STREQUAL "") - # Directory ref should not be empty, use local dir if they are the same - set(ARCH_BINARY_SUBDIR .) -endif() - -# Capture critical cache variables from the build environment so they can be -# used in custom makefiles that are invoked from here as custom targets -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/edstool-buildenv.d.in - ${CMAKE_BINARY_DIR}/obj/edstool-buildenv.d -) # The EDS toolchain generates several outputs, C headers # as well as C libraries. Scripts are included to not only @@ -82,9 +66,9 @@ set_target_properties(cfe_edsdb_static PROPERTIES # depends on the EDS datasheets in use, so as a result the scripts also generate a # makefile fragment which has the appropriate list of files to build. Back in this # cmake world this is treated as an imported target so it can be referenced like any other. -add_library(cfe_missionlib_interfacedb_static STATIC IMPORTED GLOBAL) -set_target_properties(cfe_missionlib_interfacedb_static PROPERTIES - IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_interfacedb${CMAKE_STATIC_LIBRARY_SUFFIX}" +add_library(cfe_missionlib_sb_dispatchdb_static STATIC IMPORTED GLOBAL) +set_target_properties(cfe_missionlib_sb_dispatchdb_static PROPERTIES + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_sb_dispatchdb${CMAKE_STATIC_LIBRARY_SUFFIX}" ) # The missionlib runtime contains the code that converts TopicID (functional level identifier) @@ -97,7 +81,7 @@ set_target_properties(cfe_missionlib_runtime_static PROPERTIES # All of the imported libraries are products of the EDS tool add_dependencies(cfe_edsdb_static edstool-execute) -add_dependencies(cfe_missionlib_interfacedb_static edstool-execute) +add_dependencies(cfe_missionlib_sb_dispatchdb_static edstool-execute) add_dependencies(cfe_missionlib_runtime_static edstool-execute) @@ -113,9 +97,9 @@ if (SUPPORTS_SHARED_LIBS) set_target_properties(cfe_edsdb_shared PROPERTIES IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_db${CMAKE_SHARED_LIBRARY_SUFFIX}" ) - add_library(cfe_missionlib_interfacedb_shared STATIC IMPORTED GLOBAL) - set_target_properties(cfe_missionlib_interfacedb_shared PROPERTIES - IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_interfacedb${CMAKE_SHARED_LIBRARY_SUFFIX}" + add_library(cfe_missionlib_sb_dispatchdb_shared STATIC IMPORTED GLOBAL) + set_target_properties(cfe_missionlib_sb_dispatchdb_shared PROPERTIES + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_sb_dispatchdb${CMAKE_SHARED_LIBRARY_SUFFIX}" ) add_library(cfe_missionlib_runtime_shared SHARED IMPORTED GLOBAL) set_target_properties(cfe_missionlib_runtime_shared PROPERTIES @@ -123,7 +107,7 @@ if (SUPPORTS_SHARED_LIBS) ) add_dependencies(cfe_edsdb_shared edstool-execute) - add_dependencies(cfe_missionlib_interfacedb_shared edstool-execute) + add_dependencies(cfe_missionlib_sb_dispatchdb_shared edstool-execute) add_dependencies(cfe_missionlib_runtime_shared edstool-execute) # Install the EDS missionlib library objects -- @@ -131,7 +115,7 @@ if (SUPPORTS_SHARED_LIBS) # For CFE itself it is statically linked if necessary. if(IS_CFS_MISSION_BUILD) install(FILES - "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_interfacedb${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_sb_dispatchdb${CMAKE_SHARED_LIBRARY_SUFFIX}" "${CMAKE_BINARY_DIR}/obj/${EDS_FILE_PREFIX}_db${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION lib) @@ -148,3 +132,14 @@ add_subdirectory(python) # target names do not exactly match CFE expectations. add_library(edslib ALIAS edslib_minimal) add_library(missionlib ALIAS cfe_missionlib) + +if (ENABLE_UNIT_TESTS) + + add_library(ut_eds_db STATIC IMPORTED GLOBAL) + set_target_properties(ut_eds_db PROPERTIES + IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/obj/ut_eds_db${CMAKE_SHARED_LIBRARY_SUFFIX}" + ) + + add_dependencies(ut_eds_db edstool-execute-ut) + +endif() \ No newline at end of file diff --git a/cfecfs/missionlib/arch_build.cmake b/cfecfs/missionlib/arch_build.cmake index 29a3917..4547272 100644 --- a/cfecfs/missionlib/arch_build.cmake +++ b/cfecfs/missionlib/arch_build.cmake @@ -13,9 +13,14 @@ # the generated make targets from the parent build for this processor by # supplying the correct CC/AR tool +file(RELATIVE_PATH EDSTOOL_ARCH_SUBDIR "${MISSION_BINARY_DIR}" "${CMAKE_BINARY_DIR}") + add_custom_target(edstool-execute COMMAND "\$(MAKE)" - ARCH_BINARY_DIR=${CMAKE_BINARY_DIR} + BUILD_CONFIG="${BUILD_CONFIG_${TARGETSYSTEM}}" + EDSTOOL_PROJECT_NAME=${MISSION_NAME} + O="${EDSTOOL_ARCH_SUBDIR}/obj" + S="src" -f ${missionlib_MISSION_DIR}/cmake/edstool-execute-arch.mk all WORKING_DIRECTORY @@ -23,3 +28,19 @@ add_custom_target(edstool-execute DEPENDS missionlib-runtime-install ) + +if (ENABLE_UNIT_TESTS) + add_custom_target(edstool-execute-ut + COMMAND "\$(MAKE)" + BUILD_CONFIG="${BUILD_CONFIG_${TARGETSYSTEM}}" + EDSTOOL_PROJECT_NAME="ut" + O="${EDSTOOL_ARCH_SUBDIR}/obj" + S="src" + -f ${missionlib_MISSION_DIR}/cmake/edstool-execute-arch.mk + db_objects + WORKING_DIRECTORY + ${MISSION_BINARY_DIR} + DEPENDS + missionlib-runtime-install + ) +endif() diff --git a/cfecfs/missionlib/cmake/cfe_mission_eds_interface_parameters.h.in b/cfecfs/missionlib/cmake/cfe_mission_eds_interface_parameters.h.in index 46cde2f..5fb767a 100644 --- a/cfecfs/missionlib/cmake/cfe_mission_eds_interface_parameters.h.in +++ b/cfecfs/missionlib/cmake/cfe_mission_eds_interface_parameters.h.in @@ -1,12 +1,6 @@ /* Generated wrapper based off the real source file location */ #include "@EDS_FILE_PREFIX@_designparameters.h" -#include "@EDS_FILE_PREFIX@_interfacedb.h" -#define CFE_SOFTWAREBUS_INTERFACE @EDS_SYMBOL_PREFIX@_SOFTWAREBUS_INTERFACE +extern const struct CFE_MissionLib_SoftwareBus_Interface @EDS_SYMBOL_PREFIX@_SOFTWAREBUS_INTERFACE; -#define EDS_DISPATCH_TABLE_ID(x) @EDS_SYMBOL_PREFIX@_DispatchTableId_ ## x -#define EDS_DISPATCH_TABLE_ID_MAX @EDS_SYMBOL_PREFIX@_DispatchTableId_MAX -#define EDS_INTERFACE_ID(x) @EDS_SYMBOL_PREFIX@_InterfaceId_ ## x -#define EDS_INTERFACE_ID_MAX @EDS_SYMBOL_PREFIX@_InterfaceId_MAX -#define EDS_INDICATION_ID(x) @EDS_SYMBOL_PREFIX@_IndicationId_ ## x ## _VALUE -#define EDS_INDICATION_ID_MAX @EDS_SYMBOL_PREFIX@_IndicationId_MAX +#define CFE_SOFTWAREBUS_INTERFACE @EDS_SYMBOL_PREFIX@_SOFTWAREBUS_INTERFACE diff --git a/cfecfs/missionlib/cmake/dbobj_patternrules.mk b/cfecfs/missionlib/cmake/dbobj_patternrules.mk new file mode 100644 index 0000000..0a02dd3 --- /dev/null +++ b/cfecfs/missionlib/cmake/dbobj_patternrules.mk @@ -0,0 +1,30 @@ +# Generic pattern rules for building EDS database objects + +# +# ****************************************************************************** +# ** Pattern rule to build a single database file ** +# ****************************************************************************** +# + +include $(EDS_REPO_SOURCE_DIR)/edslib/cmake/base_patternrules.mk + +# +# ****************************************************************************** +# ** Pattern rule to build a dynamic shared object file ** +# ****************************************************************************** +# + +# This is a bit of a hack - +# Embedded targets like VxWorks and RTEMS use relocatable objects (i.e. .obj files) as +# loadable modules. These are not shared objects (.so) in the Linux/POSIX sense, and +# need a different linker flag(s) to build them. In lieu of somehow extracting the full +# linker logic that CMake uses, this just assumes a GNU-style LD, and that the file extension +# (.so or .obj) indicates the style of loadable object being used. + +$(O)/%.so: + @echo EDS: Linking shared object $(@) for $(EDSTOOL_ARCH) + $(LD) $(SHARED_LDFLAGS) -shared -o $@ $^ -L$(O) -l:$(EDS_FILE_PREFIX)_db.so + +$(O)/%.obj: + @echo EDS: Linking relocatable object $(@) for $(EDSTOOL_ARCH) + $(LD) $(SHARED_LDFLAGS) -r -o $@ $^ diff --git a/cfecfs/missionlib/cmake/edstool-buildenv.d.in b/cfecfs/missionlib/cmake/edstool-buildenv.d.in deleted file mode 100644 index 539fc28..0000000 --- a/cfecfs/missionlib/cmake/edstool-buildenv.d.in +++ /dev/null @@ -1,57 +0,0 @@ -# Template for build environment variables -# See also: https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html - -# Capture critical build flags and compiler command (CC) -CMAKE := ${CMAKE_COMMAND} -CC := ${CMAKE_C_COMPILER} -CFLAGS := ${CMAKE_C_FLAGS} -AR := ${CMAKE_AR} -LD := ${CMAKE_LINKER} - -# The link flags may be different (or empty) depending on -# what type of target is being linked. -EXE_LDFLAGS := ${CMAKE_EXE_LINKER_FLAGS} -EXE_SUFFIX := ${CMAKE_EXECUTABLE_SUFFIX} -SHARED_LDFLAGS := ${CMAKE_SHARED_LINKER_FLAGS} -SHARED_SUFFIX := ${CMAKE_SHARED_LIBRARY_SUFFIX} -STATIC_LDFLAGS := ${CMAKE_STATIC_LINKER_FLAGS} -STATIC_SUFFIX := ${CMAKE_STATIC_LIBRARY_SUFFIX} - -# Capture other variables/locations -MISSION_NAME := ${MISSION_NAME} -MISSION_BINARY_DIR := ${MISSION_BINARY_DIR} -EDS_FILE_PREFIX := ${EDS_FILE_PREFIX} -EDS_SYMBOL_PREFIX := ${EDS_SYMBOL_PREFIX} -OBJDIR ?= ${ARCH_BINARY_SUBDIR}/obj -BUILD_TYPE ?= ${CMAKE_BUILD_TYPE} -EDSLIB_SOURCE_DIR := ${EDSLIB_SOURCE_DIR} -EDS_CFECFS_MISSIONLIB_SOURCE_DIR := ${EDS_CFECFS_MISSIONLIB_SOURCE_DIR} -BUILD_CONFIG := ${BUILD_CONFIG_${TARGETSYSTEM}} - -# In general most source files compiled via this method -# will require access to the EdsLib API headers. This assumes -# a gnu-style compiler option (-I), but should with clang too. -CFLAGS ?= -Wall -Werror -std=c99 -pedantic -CFLAGS += -I$(EDSLIB_SOURCE_DIR)/fsw/inc - -# Export all defined variables -export CMAKE -export CC -export CFLAGS -export AR -export LD -export EXE_LDFLAGS -export EXE_SUFFIX -export SHARED_LDFLAGS -export SHARED_SUFFIX -export STATIC_LDFLAGS -export STATIC_SUFFIX -export MISSION_NAME -export MISSION_BINARY_DIR -export OBJDIR -export BUILD_TYPE -export BUILD_CONFIG -export EDSLIB_SOURCE_DIR -export EDS_CFECFS_MISSIONLIB_SOURCE_DIR -export EDS_FILE_PREFIX -export EDS_SYMBOL_PREFIX diff --git a/cfecfs/missionlib/cmake/edstool-execute-arch.mk b/cfecfs/missionlib/cmake/edstool-execute-arch.mk index e892f9b..23578c1 100644 --- a/cfecfs/missionlib/cmake/edstool-execute-arch.mk +++ b/cfecfs/missionlib/cmake/edstool-execute-arch.mk @@ -1,30 +1,48 @@ -# NOTE: This relies on ARCH_BINARY_DIR being passed in via command line -# Specifically the edstool-buildenv.d file must be present in this dir, and -# this specifies values for OBJDIR, BUILD_CONFIG, CC/AR/LD, and others. -include $(wildcard $(ARCH_BINARY_DIR)/obj/*.d) +##################################################### +# +# CFE/CFS target build makefile helper +# This builds the EDS database objects for the target processor +# +# Importantly - the EDS tool is not re-invoked here. This simply calls +# the makefile that was generated previously when the tool was executed +# during the "mission-prebuild" phase. It uses a different "buildenv" +# file that (may) point to a different compiler, and a different "O" +# directory to put the object files into the correct arch-specific subdir. +# +##################################################### -ifeq ($(OBJDIR),) -$(error OBJDIR is not set, confirm that edstool-buildenv.d exists under $(ARCH_BINARY_DIR)/obj) +# This checks the sanity of the calling environment. If any of these fail +# it indicates a problem in the CMake target that invokes this makefile. +ifeq ($(EDSTOOL_PROJECT_NAME),) +$(error EDSTOOL_PROJECT_NAME is not defined) +endif +ifeq ($(BUILD_CONFIG),) +$(error BUILD_CONFIG not defined) +endif +ifeq ($(O),) +$(error O is not defined) +endif +ifeq ($(S),) +$(error S is not defined) endif -# The O variable in this context is localized; should not use O from the parent here. -O := $(OBJDIR) +# now include the files that should provide all the needed context +include edstool-namespace-$(EDSTOOL_PROJECT_NAME).mk +include $(O)/edstool-buildenv.mk $(wildcard $(O)/*.d) + EDSTOOL_ARCH := $(firstword $(subst ;, ,$(BUILD_CONFIG))) -LOCAL_STAMPFILE := $(MISSION_BINARY_DIR)/edstool-complete.stamp -DBOBJ_MAKEFILE := $(MISSION_BINARY_DIR)/$(EDS_FILE_PREFIX)_db_objects.mk -INTFOBJ_MAKEFILE := $(MISSION_BINARY_DIR)/$(EDS_FILE_PREFIX)_interfacedb_objects.mk +# using "wildcard" here makes it evaulate to an empty string if the file does not exist +DBOBJ_MAKEFILE := $(wildcard $(EDSTOOL_OUTPUT_DIR)/$(EDS_FILE_PREFIX)_db_objects.mk) +INTFOBJ_MAKEFILE := $(wildcard $(EDSTOOL_OUTPUT_DIR)/$(EDS_FILE_PREFIX)_sb_dispatchdb_objects.mk) -.PHONY: all db_objects interfacedb_objects -all: db_objects interfacedb_objects - @echo All target-specific EDS objects built +# These makefiles should have been generated by the top-level build +include $(DBOBJ_MAKEFILE) $(INTFOBJ_MAKEFILE) -# The stampfile dependency here should have been generated by the top-level (mission scope) -# build procedures before this build was invoked. It is listed here mainly as a sanity check. -db_objects interfacedb_objects: $(LOCAL_STAMPFILE) +.PHONY: all db_objects sb_dispatchdb_objects +all: db_objects sb_dispatchdb_objects + @echo All target-specific EDS objects built -db_objects: $(addprefix $(OBJDIR)/$(EDS_FILE_PREFIX)_db,$(SHARED_SUFFIX) $(STATIC_SUFFIX)) -interfacedb_objects: $(addprefix $(OBJDIR)/$(EDS_FILE_PREFIX)_interfacedb,$(SHARED_SUFFIX) $(STATIC_SUFFIX)) +db_objects: $(addprefix $(O)/$(EDS_FILE_PREFIX)_db,$(SHARED_SUFFIX) $(STATIC_SUFFIX)) +sb_dispatchdb_objects: $(addprefix $(O)/$(EDS_FILE_PREFIX)_sb_dispatchdb,$(SHARED_SUFFIX) $(STATIC_SUFFIX)) -# These makefiles should have been generated by the top-level build -include $(DBOBJ_MAKEFILE) $(INTFOBJ_MAKEFILE) diff --git a/cfecfs/missionlib/cmake/edstool-execute-mission.mk b/cfecfs/missionlib/cmake/edstool-execute-mission.mk deleted file mode 100644 index d8b422d..0000000 --- a/cfecfs/missionlib/cmake/edstool-execute-mission.mk +++ /dev/null @@ -1,33 +0,0 @@ -include edstool-buildenv.d -include edstool-sources.d - -LOCAL_STAMPFILE := edstool-complete.stamp -MISSION_PARAM_HEADER := inc/cfe_mission_eds_parameters.h -INTFDB_PARAM_HEADER := inc/cfe_mission_eds_interface_parameters.h -O := $(OBJDIR) - - -# NOTE: At the mission scope, this runs the full EDS tool to generate all artifacts -# As part of this, it invokes the db_objects make scripts as well, so there is noting -# extra that needs to be invoked from here. - -.PHONY: all -all: $(LOCAL_STAMPFILE) - @echo All host side global EDS objects built - -# The file lists here came from CMake, and so they have the semicolon separator, -# which needs to be changed to a space separator for interpretation here. -ALL_EDS_FILES += $(subst ;, ,$(MISSION_EDS_FILELIST)) -ALL_EDS_FILES += $(subst ;, ,$(MISSION_EDS_SCRIPTLIST)) - -$(LOCAL_STAMPFILE): $(EDSTOOL) $(MISSION_PARAM_HEADER) $(INTFDB_PARAM_HEADER) $(ALL_EDS_FILES) - +$(EDSTOOL) -DMAKE_PROGRAM="$(MAKE)" -DOBJDIR=$(OBJDIR) $(ALL_EDS_FILES) - $(CMAKE) -E touch "$(@)" - -$(MISSION_PARAM_HEADER): $(EDS_CFECFS_MISSIONLIB_SOURCE_DIR)/cmake/cfe_mission_eds_parameters.h.in obj/edstool-buildenv.d - @echo "configure_file($(<) $(@))" > src/generate_eds_parameters_h.cmake - $(CMAKE) -D MISSION_NAME=$(MISSION_NAME) -D EDS_FILE_PREFIX=$(EDS_FILE_PREFIX) -D EDS_SYMBOL_PREFIX=$(EDS_SYMBOL_PREFIX) -P src/generate_eds_parameters_h.cmake - -$(INTFDB_PARAM_HEADER): $(EDS_CFECFS_MISSIONLIB_SOURCE_DIR)/cmake/cfe_mission_eds_interface_parameters.h.in obj/edstool-buildenv.d - @echo "configure_file($(<) $(@))" > src/cfe_mission_eds_interface_parameters.cmake - $(CMAKE) -D MISSION_NAME=$(MISSION_NAME) -D EDS_FILE_PREFIX=$(EDS_FILE_PREFIX) -D EDS_SYMBOL_PREFIX=$(EDS_SYMBOL_PREFIX) -P src/cfe_mission_eds_interface_parameters.cmake diff --git a/cfecfs/missionlib/cmake/export-cmake-env.cmake b/cfecfs/missionlib/cmake/export-cmake-env.cmake new file mode 100644 index 0000000..4900886 --- /dev/null +++ b/cfecfs/missionlib/cmake/export-cmake-env.cmake @@ -0,0 +1,5 @@ + +configure_file( + ${TEMPLATE_FILE} + ${OUTPUT_FILE} +) diff --git a/cfecfs/missionlib/eds/70-cfe_sb_parameter_map.lua b/cfecfs/missionlib/eds/70-cfe_sb_parameter_map.lua index 74c80b3..1abfaee 100644 --- a/cfecfs/missionlib/eds/70-cfe_sb_parameter_map.lua +++ b/cfecfs/missionlib/eds/70-cfe_sb_parameter_map.lua @@ -35,49 +35,11 @@ -- compatible with future Flight Software code. -- ------------------------------------------------------------------------- -local makefilename = SEDS.to_filename("db_objects.mk") local objdir = SEDS.get_define("OBJDIR") or "obj" -local edsdb_basename = string.format("%s/%s", objdir, SEDS.to_filename("db")) local missionlib_basename = string.format("%s/%s", objdir, SEDS.to_filename("missionlib_runtime")) -local edsdb_load_sym = string.upper(SEDS.get_define("MISSION_NAME") or "EDS") .. "_DATABASE" -- ------------------------------------------------------------------ --- Step 0: determine list of targets to build here. --- there are potentially 3 targets: --- .a - static link version --- .so - shared link version (PIC) --- .obj - relocatable version --- ------------------------------------------------------------------ -local all_edsdb_targets = {} -for _,t in ipairs({ ".so", ".a", ".obj" }) do - all_edsdb_targets[1 + #all_edsdb_targets] = edsdb_basename .. t -end - - --- ------------------------------------------------------------------ --- Step 1: call the build tool to actually run the makefile generated by the edsdb script --- --- This should produce a valid .a and .so file. If this triggers any errors, --- the script will abort. This indicates problems in the generated source files. --- ------------------------------------------------------------------ -SEDS.execute_tool("MAKE_PROGRAM", - string.format("-C \"%s\" -f \"%s\" O=\"%s\" %s", - SEDS.get_define("MISSION_BINARY_DIR") or ".", - makefilename, - objdir, - table.concat(all_edsdb_targets," ") - ) -) - --- ------------------------------------------------------------------ --- Step 2: load the .so file just created into this running process --- The database will be attached to the "SEDS" global as the edslib member --- ------------------------------------------------------------------ -SEDS.edsdb = SEDS.load_plugin(edsdb_basename .. ".so"); -SEDS.edslib = SEDS.attach_db(SEDS.edsdb[edsdb_load_sym]) - --- ------------------------------------------------------------------ --- Step 3: build and load the CFS missionlib runtime library +-- Step 1: build and load the CFS missionlib runtime library -- -- Specifically we need the "runtime" code that maps interfaces onto the software bus -- This code can then be called to get the corresponding msgid values to produce the @@ -91,7 +53,7 @@ SEDS.execute_tool("MAKE_PROGRAM", "missionlib-runtime-install") local cfe_sb_runtime = SEDS.load_plugin(missionlib_basename .. ".so"); -- ------------------------------------------------------------------ --- Step 4: populate the interface tree using the missionlib functions +-- Step 2: populate the interface tree using the missionlib functions -- ------------------------------------------------------------------ -- Helper function to collect the parameters for a given interface diff --git a/cfecfs/missionlib/eds/75-cfe_sb_dispatch_tables.lua b/cfecfs/missionlib/eds/75-cfe_sb_dispatch_tables.lua index d93f857..1c27cae 100644 --- a/cfecfs/missionlib/eds/75-cfe_sb_dispatch_tables.lua +++ b/cfecfs/missionlib/eds/75-cfe_sb_dispatch_tables.lua @@ -28,7 +28,7 @@ -- messages from the software bus to the local application. -- ------------------------------------------------------------------------- -local global_sym_prefix = SEDS.get_define("MISSION_NAME") +local global_sym_prefix = SEDS.get_define("EDSTOOL_PROJECT_NAME") global_sym_prefix = global_sym_prefix and string.upper(global_sym_prefix) or "EDS" local components = SEDS.mts_info.component_set @@ -169,30 +169,33 @@ local function write_interface_header(hdrout,comp) local desc = comp:get_flattened_name() for reqintf in comp:iterate_subtree("REQUIRED_INTERFACE") do - -- Export information by saving in the DOM tree - reqintf.mapping_info = get_mapping_info(desc, reqintf.type) - - -- This adds an "intf_commands" member that is a list of descriptors for the commands on this intf - -- The descriptor has the following info in it: - -- refnode : points to the cmd node in the DOM tree - -- args : a list of arguments to that command (generally always length of 1 in CFE) - -- subcommand_arg : the index of the argument that has subcommands (derivatives). Should be 1 for CFE ground commands, nil on everything else. - write_reqintf_data(hdrout,reqintf) - - -- If this yielded a non-empty list, then merge it with the component_intftypes - -- This builds the complete set of interface types within this component - if (#reqintf.intf_commands > 0) then - local compcmds = component_intftypes[reqintf.type] + -- Only do this on SB functional interfaces + local intf_type_str = reqintf.type:get_qualified_name() + if (intf_type_str == "CFE_SB/Telemetry" or intf_type_str == "CFE_SB/Telecommand") then + -- Export information by saving in the DOM tree + reqintf.mapping_info = get_mapping_info(desc, reqintf.type) + + -- This adds an "intf_commands" member that is a list of descriptors for the commands on this intf + -- The descriptor has the following info in it: + -- refnode : points to the cmd node in the DOM tree + -- args : a list of arguments to that command (generally always length of 1 in CFE) + -- subcommand_arg : the index of the argument that has subcommands (derivatives). Should be 1 for CFE ground commands, nil on everything else. + write_reqintf_data(hdrout,reqintf) + + -- If this yielded a non-empty list, then merge it with the component_intftypes + -- This builds the complete set of interface types within this component + if (#reqintf.intf_commands > 0) then + local compcmds = component_intftypes[reqintf.type] + + if (not compcmds) then + compcmds = {} + component_intftypes[reqintf.type] = compcmds + end - if (not compcmds) then - compcmds = {} - component_intftypes[reqintf.type] = compcmds + -- Append this set of commands to the + append_list(compcmds, reqintf.intf_commands) end - - -- Append this set of commands to the - append_list(compcmds, reqintf.intf_commands) end - end -- This gets the complete set of interface types in this component in a sorted order @@ -229,6 +232,8 @@ local function write_dispatch_header(hdrout,entry) -- there is just a single one. local command_count = SEDS.count_results(entry.intftype:iterate_subtree("COMMAND")) + local basenode_comp = entry.component:find_parent(SEDS.namespace_filter) + local basenode_declintf = entry.intftype:find_parent(SEDS.namespace_filter) hdrout:start_group(string.format("static inline int32_t EdsDispatch_%s(",entry.dispatchid)) hdrout:write(string.format("%-65s *Message,", "const CFE_SB_Buffer_t")) @@ -240,14 +245,13 @@ local function write_dispatch_header(hdrout,entry) end hdrout:end_group(")") hdrout:start_group("{") - hdrout:start_group("return CFE_MSG_EdsDispatch(") - hdrout:write(string.format("%s_InterfaceId_%s,", global_sym_prefix, entry.intftype:get_ctype_basename())) - if (command_count > 1) then - hdrout:write(string.format("IndicationId - %s_%s,", global_sym_prefix, entry.intftype:get_ctype_basename("IndicationId") .. "_BASE")) - else - hdrout:write("1,") - end - hdrout:write(string.format("%s_DispatchTableId_%s,", global_sym_prefix, entry.dispatchid)) + hdrout:start_group("return CFE_EDSMSG_Dispatch(") + hdrout:write(string.format("EDSLIB_INTF_ID(%s_INDEX_%s, %s),", + global_sym_prefix, SEDS.to_macro_name(basenode_declintf.name), + entry.intftype:get_flattened_name("DECLARATION"))) + hdrout:write(string.format("EDSLIB_INTF_ID(%s_INDEX_%s, %s),", + global_sym_prefix, SEDS.to_macro_name(basenode_comp.name), + entry.component:get_flattened_name("INSTANCE"))) hdrout:write("Message,") hdrout:write("DispatchTable") hdrout:end_group(");") @@ -282,8 +286,8 @@ for ds in SEDS.root:iterate_children(SEDS.basenode_filter) do hdrout = SEDS.output_open(SEDS.to_filename("dispatcher.h", ds.name), ds.xml_filename) - hdrout:write(string.format("#include \"cfe_msg_dispatcher.h\"")) - hdrout:write(string.format("#include \"%s\"", SEDS.to_filename("interfacedb.h", global_sym_prefix))) + hdrout:write(string.format("#include \"edsmsg_dispatcher.h\"")) + hdrout:write(string.format("#include \"edslib_id.h\"")) hdrout:write(string.format("#include \"%s\"", SEDS.to_filename("interface.h", ds.name))) hdrout:add_whitespace(1) diff --git a/cfecfs/missionlib/eds/76-cfe_sb_interfacedb_global.lua b/cfecfs/missionlib/eds/76-cfe_sb_interfacedb_global.lua deleted file mode 100644 index 80ee943..0000000 --- a/cfecfs/missionlib/eds/76-cfe_sb_interfacedb_global.lua +++ /dev/null @@ -1,262 +0,0 @@ --- --- LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation --- --- Copyright (c) 2020 United States Government as represented by --- the Administrator of the National Aeronautics and Space Administration. --- All Rights Reserved. --- --- Licensed under the Apache License, Version 2.0 (the "License"); --- you may not use this file except in compliance with the License. --- You may obtain a copy of the License at --- --- http://www.apache.org/licenses/LICENSE-2.0 --- --- Unless required by applicable law or agreed to in writing, software --- distributed under the License is distributed on an "AS IS" BASIS, --- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. --- See the License for the specific language governing permissions and --- limitations under the License. --- - - --- ------------------------------------------------------------------------- --- Lua implementation of the "dispatch tables" as part of the CFS/MissionLib --- --- In this area the EDS processing becomes very specific to CFE/CFS --- and how this application will actually use it. The objective is --- to generate the appropriate data structures for "dispatching" --- messages from the software bus to the local application. --- ------------------------------------------------------------------------- - -local global_sym_prefix = SEDS.get_define("MISSION_NAME") -global_sym_prefix = global_sym_prefix and string.upper(global_sym_prefix) or "EDS" - -local components = SEDS.mts_info.component_set -local topicid_table = SEDS.mts_info.mts_component.topicid_table - -local total_intfs = SEDS.mts_info.total_intfs -local interface_list = {} - - --- ----------------------------------------------------- --- helper function to generate software bus component/interface definitions --- ----------------------------------------------------- - --- ----------------------------------------------------- --- helper function to generate software bus component/interface definitions --- ----------------------------------------------------- - - --- ----------------------------------------------------- --- Main routine starts here --- ----------------------------------------------------- ---[[ ---]] -local hdrout = SEDS.output_open(SEDS.to_filename("interfacedb.h")) - -hdrout:write("enum") -hdrout:start_group("{") -for ds in SEDS.root:iterate_children(SEDS.basenode_filter) do - for intftype in ds:iterate_subtree("DECLARED_INTERFACE") do - interface_list[1 + #interface_list] = intftype - local command_list = {} - for cmd in intftype:iterate_subtree("COMMAND") do - command_list[1 + #command_list] = cmd - end - if (#command_list > 0) then - hdrout:write(string.format("%s_%s_BASE,", global_sym_prefix, intftype:get_ctype_basename("IndicationId"))) - for _,cmd in ipairs(command_list) do - hdrout:write(string.format("%s_%s_VALUE,", global_sym_prefix, cmd:get_ctype_basename("IndicationId"))) - end - hdrout:add_whitespace(1) - end - end -end -hdrout:write(string.format("%s_%s_MAX,", global_sym_prefix, "IndicationId")) -hdrout:end_group("};") -hdrout:add_whitespace(1) - -hdrout:write("enum") -hdrout:start_group("{") -hdrout:write(string.format("%s_DispatchTableId_RESERVED = 0,", global_sym_prefix)) -for _,entry in ipairs(total_intfs) do - hdrout:write(string.format("%s_DispatchTableId_%s,", global_sym_prefix, entry.dispatchid)) -end -hdrout:write(string.format("%s_DispatchTableId_MAX", global_sym_prefix)) -hdrout:end_group("};") -hdrout:add_whitespace(1) - -hdrout:write("enum") -hdrout:start_group("{") -hdrout:write(string.format("%s_InterfaceId_RESERVED = 0,", global_sym_prefix)) -for _,intftype in ipairs(interface_list) do - hdrout:write(string.format("%s_InterfaceId_%s,", global_sym_prefix, intftype:get_ctype_basename())) -end -hdrout:write(string.format("%s_InterfaceId_MAX", global_sym_prefix)) -hdrout:end_group("};") -hdrout:add_whitespace(1) - -hdrout:write(string.format("extern const struct CFE_MissionLib_SoftwareBus_Interface %s_SOFTWAREBUS_INTERFACE;", global_sym_prefix)) - - -SEDS.output_close(hdrout) - - -local dbout = SEDS.output_open(SEDS.to_filename("interfacedb_impl.c")) - -dbout:write("#include \"edslib_database_types.h\"") -dbout:write("#include \"cfe_missionlib_database_types.h\"") -dbout:write(string.format("#include \"%s\"",SEDS.to_filename("interfacedb.h"))) -dbout:write(string.format("#include \"%s\"",SEDS.to_filename("master_index.h"))) -for ds in SEDS.root:iterate_children(SEDS.basenode_filter) do - dbout:write(string.format("#include \"%s\"",SEDS.to_filename("interface.h", ds.name))) -end -dbout:add_whitespace(1) - -for tid = 1,SEDS.get_define("CFE_MISSION/MAX_TOPICID") do - local chain = topicid_table[tid] - if (chain) then - local intf = chain.binding.reqintf - local cmdlist = intf.intf_commands or {} - local arg_list = {} - for j,cmd in ipairs(cmdlist) do - if (#cmd.args > 0) then - arg_list[j] = string.format("%s_%s_ARGUMENT_LIST",intf:get_flattened_name(), cmd.refnode.name) - dbout:write(string.format("static const CFE_MissionLib_Argument_Entry_t %s[] =", arg_list[j])) - dbout:start_group("{") - for _,arg in ipairs(cmd.args) do - dbout:append_previous(",") - dbout:start_group("{") - local ds = arg.type:find_parent(SEDS.basenode_filter) - dbout:write(string.format(".AppIndex = %s,", ds.edslib_refobj_global_index)) - dbout:write(string.format(".TypeIndex = %s", arg.type.edslib_refobj_local_index)) - dbout:end_group("}") - end - dbout:end_group("};") - dbout:add_whitespace(1) - end - end - - for j,cmd in ipairs(cmdlist) do - if (cmd.subcommand_arg) then - local derivlist = cmd.args[cmd.subcommand_arg].type.edslib_derivtable_list - dbout:write(string.format("static const CFE_MissionLib_Subcommand_Entry_t %s_%s_SUBCOMMAND_LIST[] =", - intf:get_flattened_name(), cmd.refnode.name)) - dbout:start_group("{") - for _,deriv in ipairs(derivlist) do - dbout:append_previous(",") - dbout:start_group("{") - dbout:write(string.format(".DispatchOffset = offsetof(struct %s, %s)", - intf:get_ctype_basename("Commands"), deriv.name .. "_" .. cmd.refnode.name)) - dbout:end_group("}") - end - dbout:end_group("};") - dbout:add_whitespace(1) - end - end - - local cmd_def = string.format("%s_COMMANDS",intf:get_flattened_name()) - dbout:write(string.format("static const CFE_MissionLib_Command_Definition_Entry_t %s[] =",cmd_def)) - dbout:start_group("{") - for j,cmd in ipairs(cmdlist) do - dbout:append_previous(",") - dbout:start_group("{") - if (cmd.subcommand_arg) then - dbout:write(string.format(".SubcommandArg = %d,", cmd.subcommand_arg)) - dbout:write(string.format(".SubcommandCount = %d,", - #cmd.args[cmd.subcommand_arg].type.edslib_derivtable_list)) - dbout:write(string.format(".SubcommandList = %s_%s_SUBCOMMAND_LIST", - intf:get_flattened_name(), cmd.refnode.name)) - end - if (arg_list[j]) then - dbout:append_previous(",") - dbout:write(string.format(".ArgumentList = %s", arg_list[j])) - end - dbout:end_group("}") - end - dbout:end_group("};") - dbout:add_whitespace(1) - end -end - -local objname = string.format("%s_TOPICID_LOOKUP", global_sym_prefix) -local base_topicid = 1 -dbout:write(string.format("static const CFE_MissionLib_TopicId_Entry_t %s[%d] =", objname, SEDS.get_define("CFE_MISSION/MAX_TOPICID") - base_topicid)) -dbout:start_group("{") -for tid = 1,SEDS.get_define("CFE_MISSION/MAX_TOPICID") do - local chain = topicid_table[tid] - if (chain) then - dbout:append_previous(",") - dbout:write(string.format("[%d] =", chain.tctm.TopicId - base_topicid)) - dbout:start_group("{") - dbout:write(string.format(".DispatchTableId = %s_DispatchTableId_%s,", global_sym_prefix, chain.binding.reqintf.mapping_info.dispatchid)) - dbout:write(string.format(".DispatchStartOffset = offsetof(struct %s,%s),", - chain.binding.reqintf.mapping_info.dispatchtype, - chain.binding.reqintf.name)) - dbout:write(string.format(".InterfaceId = %s_InterfaceId_%s,", global_sym_prefix, chain.binding.reqintf.type:get_ctype_basename())) - dbout:write(string.format(".TopicName = \"%s\",", chain.binding.reqintf:get_qualified_name())) - dbout:write(string.format(".CommandList = %s_COMMANDS",chain.binding.reqintf:get_flattened_name())) - dbout:end_group("}") - end -end -dbout:end_group("};") -dbout:add_whitespace(1) - -dbout:section_marker("Interface Definitions") -for _,intftype in ipairs(interface_list) do - if (intftype:find_first("COMMAND")) then - dbout:write(string.format("static const CFE_MissionLib_Command_Prototype_Entry_t %s_BASE_COMMAND_LIST[] =", intftype:get_flattened_name())) - dbout:start_group("{") - for cmd in intftype:iterate_subtree("COMMAND") do - dbout:append_previous(",") - dbout:start_group("{") - dbout:write(string.format(".CommandName = \"%s\",", cmd.name)) - dbout:write(string.format(".NumArguments = %d", SEDS.count_results(cmd:iterate_subtree("ARGUMENT")))) - dbout:end_group("}") - end - dbout:end_group("};") - dbout:add_whitespace(1) - end -end - -dbout:write(string.format("static const CFE_MissionLib_InterfaceId_Entry_t %s_INTERFACEID_LOOKUP[] =", global_sym_prefix)) -dbout:start_group("{") -for _,intftype in ipairs(interface_list) do - local command_count = SEDS.count_results(intftype:iterate_subtree("COMMAND")) - local intfname = intftype:get_qualified_name() - dbout:append_previous(",") - dbout:start_group("{") - dbout:write(string.format(".NumCommands = %d,", command_count)) - dbout:write(string.format(".InterfaceName = \"%s\",", intfname)) - if (command_count > 0) then - dbout:write(string.format(".CommandList = %s,", intftype:get_flattened_name() .. "_BASE_COMMAND_LIST")) - end - -- Only include topicid lookup info on the TC/TM interfaces - -- this is a bit of a hack as this doesn't really belong here. - if (intfname == "CFE_SB/Telecommand" or intfname == "CFE_SB/Telemetry") then - dbout:write(string.format(".NumTopics = %d,", SEDS.get_define("CFE_MISSION/MAX_TOPICID") - base_topicid)) - dbout:write(string.format(".TopicList = %s_TOPICID_LOOKUP,", global_sym_prefix)) - end - dbout:end_group("}") -end -dbout:end_group("};") -dbout:add_whitespace(1) - -dbout:section_marker("Instance Name Table") -dbout:write("#define DEFINE_TGTNAME(x) #x,") -dbout:write(string.format("static const char * const %s_INSTANCE_LIST[] =", global_sym_prefix)) -dbout:write("{") -dbout:write("#include \"cfe_mission_tgtnames.inc\"") -dbout:write(" NULL") -dbout:write("};") - -dbout:section_marker("Summary Object") -dbout:write(string.format("const struct CFE_MissionLib_SoftwareBus_Interface %s_SOFTWAREBUS_INTERFACE =", global_sym_prefix)) -dbout:start_group("{") - dbout:write(string.format(".NumInterfaces = %d,", #interface_list)) - dbout:write(string.format(".InterfaceList = %s_INTERFACEID_LOOKUP,", global_sym_prefix)) - dbout:write(string.format(".InstanceList = %s_INSTANCE_LIST", global_sym_prefix)) -dbout:end_group("};") -dbout:add_whitespace(1) - -SEDS.output_close(dbout) diff --git a/cfecfs/missionlib/eds/76-cfe_sb_topicdb_global.lua b/cfecfs/missionlib/eds/76-cfe_sb_topicdb_global.lua new file mode 100644 index 0000000..3ce834c --- /dev/null +++ b/cfecfs/missionlib/eds/76-cfe_sb_topicdb_global.lua @@ -0,0 +1,104 @@ +-- +-- LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation +-- +-- Copyright (c) 2020 United States Government as represented by +-- the Administrator of the National Aeronautics and Space Administration. +-- All Rights Reserved. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + + +-- ------------------------------------------------------------------------- +-- Lua implementation of the TopicID table generator as part of the CFS/MissionLib +-- +-- In this area the EDS processing becomes very specific to CFE/CFS +-- and how this application will actually use it. The objective is +-- to generate the appropriate data structures for "dispatching" +-- messages from the software bus to the local application. +-- ------------------------------------------------------------------------- + +local global_sym_prefix = SEDS.get_define("EDSTOOL_PROJECT_NAME") +global_sym_prefix = global_sym_prefix and string.upper(global_sym_prefix) or "EDS" + +local topicid_table = SEDS.mts_info.mts_component.topicid_table + + +-- ----------------------------------------------------- +-- Main routine starts here +-- ----------------------------------------------------- +local dbout = SEDS.output_open(SEDS.to_filename("sb_topicdb_impl.c")) + +dbout:write("#include \"edslib_database_types.h\"") +dbout:write("#include \"cfe_missionlib_database_types.h\"") +dbout:write(string.format("#include \"%s\"",SEDS.to_filename("master_index.h"))) +for ds in SEDS.root:iterate_children(SEDS.basenode_filter) do + dbout:write(string.format("#include \"%s\"",SEDS.to_filename("interface.h", ds.name))) +end +dbout:add_whitespace(1) + +for tid = 1,SEDS.get_define("CFE_MISSION/MAX_TOPICID") do + local chain = topicid_table[tid] + if (chain) then + local intf = chain.binding.reqintf + local cmdlist = intf.intf_commands or {} + local arg_list = {} + + for j,cmd in ipairs(cmdlist) do + if (cmd.subcommand_arg) then + local derivlist = cmd.args[cmd.subcommand_arg].type.edslib_derivtable_list + chain.dispatch_symbol_name = string.format("EDS_DISPATCHTABLE_%s_%s", intf:get_flattened_name(), cmd.refnode.name) + chain.dispatch_table_size = #derivlist + dbout:write(string.format("static const CFE_MissionLib_DispatchTable_Entry_t %s[] =",chain.dispatch_symbol_name)) + dbout:start_group("{") + for _,deriv in ipairs(derivlist) do + dbout:append_previous(",") + dbout:start_group("{") + dbout:write(string.format(".DispatchOffset = offsetof(struct %s, %s)", + intf:get_ctype_basename("Commands"), deriv.name .. "_" .. cmd.refnode.name)) + dbout:end_group("}") + end + dbout:end_group("};") + dbout:add_whitespace(1) + end + end + end +end + +local objname = string.format("%s_TOPICID_LOOKUP", global_sym_prefix) +dbout:write(string.format("const CFE_MissionLib_TopicId_Entry_t %s[%d] =", objname, SEDS.get_define("CFE_MISSION/MAX_TOPICID"))) +dbout:start_group("{") +for tid = 1,SEDS.get_define("CFE_MISSION/MAX_TOPICID") do + local chain = topicid_table[tid] + if (chain) then + dbout:append_previous(",") + dbout:write(string.format("[%d] =", chain.tctm.TopicId)) + dbout:start_group("{") + dbout:write(string.format(".DispatchStartOffset = offsetof(struct %s,%s),", + chain.binding.reqintf.mapping_info.dispatchtype, + chain.binding.reqintf.name)) + + if (chain.dispatch_table_size) then + dbout:write(string.format(".LocalDispatchTable = %s,",chain.dispatch_symbol_name)) + dbout:write(string.format(".LocalDispatchSize = %d,",chain.dispatch_table_size)) + end + + dbout:write(string.format(".InterfaceRefObj = %s", chain.binding.reqintf.edslib_refobj_intfdb_initializer)) + + dbout:end_group("}") + end +end +dbout:end_group("};") +dbout:add_whitespace(1) + +SEDS.output_close(dbout) diff --git a/cfecfs/missionlib/eds/78-cfe_sb_global.lua b/cfecfs/missionlib/eds/78-cfe_sb_global.lua new file mode 100644 index 0000000..c9243d6 --- /dev/null +++ b/cfecfs/missionlib/eds/78-cfe_sb_global.lua @@ -0,0 +1,64 @@ +-- +-- LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation +-- +-- Copyright (c) 2020 United States Government as represented by +-- the Administrator of the National Aeronautics and Space Administration. +-- All Rights Reserved. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + + +-- ------------------------------------------------------------------------- +-- Lua implementation of the "dispatch tables" as part of the CFS/MissionLib +-- +-- In this area the EDS processing becomes very specific to CFE/CFS +-- and how this application will actually use it. The objective is +-- to generate the appropriate data structures for "dispatching" +-- messages from the software bus to the local application. +-- ------------------------------------------------------------------------- + +local global_sym_prefix = SEDS.get_define("EDSTOOL_PROJECT_NAME") +global_sym_prefix = global_sym_prefix and string.upper(global_sym_prefix) or "EDS" + +local dbout = SEDS.output_open(SEDS.to_filename("sb_global_impl.c")) + +dbout:write("#include \"edslib_database_types.h\"") +dbout:write("#include \"cfe_missionlib_database_types.h\"") +dbout:write(string.format("#include \"%s\"",SEDS.to_filename("master_index.h"))) +dbout:add_whitespace(1) + + +dbout:write(string.format("extern CFE_MissionLib_TopicId_Entry_t %s_TOPICID_LOOKUP[];", global_sym_prefix)) +dbout:add_whitespace(1) + +dbout:section_marker("Instance Name Table") +dbout:write("#define DEFINE_TGTNAME(x) #x,") +dbout:write(string.format("static const char * const %s_INSTANCE_LIST[] =", global_sym_prefix)) +dbout:write("{") +dbout:write("#include \"cfe_mission_tgtnames.inc\"") +dbout:write(" NULL") +dbout:write("};") + +dbout:section_marker("Summary Object") +dbout:write(string.format("const struct CFE_MissionLib_SoftwareBus_Interface %s_SOFTWAREBUS_INTERFACE =", global_sym_prefix)) +dbout:start_group("{") + dbout:write(string.format(".ParentGD = &%s_DATABASE,", global_sym_prefix)) + dbout:write(string.format(".InstanceList = %s_INSTANCE_LIST,", global_sym_prefix)) + dbout:write(string.format(".NumInstances = (sizeof(%s_INSTANCE_LIST) / sizeof(const char *)) - 1,", global_sym_prefix)) + dbout:write(string.format(".NumTopics = %d,", SEDS.get_define("CFE_MISSION/MAX_TOPICID"))) + dbout:write(string.format(".TopicList = %s_TOPICID_LOOKUP", global_sym_prefix)) +dbout:end_group("};") +dbout:add_whitespace(1) + +SEDS.output_close(dbout) diff --git a/cfecfs/missionlib/eds/80-build_cfe_sb_interfacedb.lua b/cfecfs/missionlib/eds/80-build_cfe_sb_interfacedb.lua index a38430c..9e512a1 100644 --- a/cfecfs/missionlib/eds/80-build_cfe_sb_interfacedb.lua +++ b/cfecfs/missionlib/eds/80-build_cfe_sb_interfacedb.lua @@ -19,12 +19,13 @@ -- -- ------------------------------------------------------------------------- --- Lua script to build the generated interfacedb C objects +-- Lua script to build the generated sb_dispatchdb C objects -- ------------------------------------------------------------------------- local objdir = SEDS.get_define("OBJDIR") or "obj" -local libname = SEDS.to_filename("interfacedb") -local makefilename = SEDS.to_filename("interfacedb_objects.mk") +local srcdir = SEDS.get_define("SRCDIR") or "src" +local libname = SEDS.to_filename("sb_dispatchdb") +local makefilename = SEDS.to_filename("sb_dispatchdb_objects.mk") local libtypes = { ".a", ".so", ".obj" } local function get_objnames(n,prefix,suffix) @@ -36,17 +37,18 @@ local function get_objnames(n,prefix,suffix) end -- ------------------------------------------------ --- Generate a makefile for the interfacedb library --- This is currently only one file, the "interfacedb_impl" +-- Generate a makefile for the sb_dispatchdb library -- ------------------------------------------------ local output = SEDS.output_open(makefilename) -output:write("include $(O)/edstool-buildenv.d $(wildcard $(O)/*.d)") -output:write("include $(EDSLIB_SOURCE_DIR)/cmake/dbobj_patternrules.mk") +output:write("include edstool-namespace-$(EDSTOOL_PROJECT_NAME).mk") +output:write("include $(O)/edstool-buildenv.mk $(wildcard $(O)/*.d)") +output:write("include $(EDS_REPO_SOURCE_DIR)/cfecfs/missionlib/cmake/dbobj_patternrules.mk") output:add_whitespace(1) output:write("# Interface DB Object") -output:write(string.format("%s: $(O)/%s", +output:write(string.format("%s: $(O)/%s $(O)/%s", get_objnames(libname,"$(O)",libtypes), - SEDS.to_filename("interfacedb_impl.o"))) + SEDS.to_filename("sb_topicdb_impl.o"), + SEDS.to_filename("sb_global_impl.o"))) output:add_whitespace(1) SEDS.output_close(output) @@ -55,10 +57,11 @@ SEDS.output_close(output) -- interfacdb using the makefile generated above. -- ------------------------------------------------ SEDS.execute_tool("MAKE_PROGRAM", - string.format("-C \"%s\" -f \"%s\" O=\"%s\" %s", - SEDS.get_define("MISSION_BINARY_DIR") or ".", + string.format("-C \"%s\" -f \"%s\" O=\"%s\" S=\"%s\" %s", + SEDS.get_define("EDSTOOL_OUTPUT_DIR") or ".", makefilename, objdir, + srcdir, get_objnames(libname,objdir,libtypes) ) ) diff --git a/cfecfs/missionlib/eds/86-write_dds_idl.lua b/cfecfs/missionlib/eds/86-write_dds_idl.lua new file mode 100644 index 0000000..277786a --- /dev/null +++ b/cfecfs/missionlib/eds/86-write_dds_idl.lua @@ -0,0 +1,544 @@ +local write_idl_field +local write_idl_array_members +local write_idl_container_members + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: assemble the combined element name +-- -- ------------------------------------------------------------------------- +local append_qual_prefix = function(old_prefix, add_prefix) + local merged_prefix + + if (old_prefix ~= nil) then + merged_prefix = tostring(old_prefix) .. "_" + else + merged_prefix = "" + end + + if (add_prefix ~= nil) then + merged_prefix = merged_prefix .. tostring(add_prefix) + else + merged_prefix = merged_prefix .. "?" + end + + return merged_prefix +end + +local get_annotation = function(attribs) + local min = attribs.min + local max = attribs.max + + if (min == nil and max == nil) then + -- No bounds, no annotation + return "" + elseif (min == nil) then + return string.format("@max(%.0f)", max) + elseif (max == nil) then + return string.format("@min(%.0f)", min) + else + return string.format("@range(min=%.0f, max=%.0f)", min, max) + end +end + +local update_field_name = function(type, name) + -- Some things that are legal in EDS are not according to fastddsgen + -- Resolve the idiosyncracies here + + if string.lower(type) == string.lower(name) then + -- Type and field names cannot match, case insensitive + name = "_" .. name + elseif string.lower(name) == string.lower("EventType") or + string.lower(name) == string.lower("BitMask") then + -- Handle reserved names + name = "_" .. name + end + + return name +end + +local write_field = function(output, type, name, count) + local count_str = "" + if (count > 1) then + count_str = string.format("[%u]", count) + end + + output:write(string.format("%s %s%s;", type, update_field_name(type, name), count_str)) +end + +function byte_align(size_bits, count) + local total_bits = size_bits * count + + -- Round up to the nearest full byte + local size_bytes = math.ceil(size_bits / 8) + + local new_count = math.ceil(total_bits / (size_bytes * 8)) + + return size_bytes, new_count +end + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: Write a single line-item to a CMD definition (APPEND_PARAMETER) +-- -- ------------------------------------------------------------------------- +write_idl_field = function(output, attribs, count) + if count == nil then + count = 1 + end + + -- Ensure that the field is byte-aligned + assert((attribs.bitsize * count) % 8 == 0) + -- Convert any arrays of bits into smaller arrays of bytes + local byte_size, count = byte_align(attribs.bitsize, count) + + -- For unknown reasons, when a struct has a base type defined it shows up + -- here as "_". Ignore it because we already write the inheritance relationship + if (attribs.name == "_") then + return + end + + if (attribs.descr) then + output:write(string.format("// %s", attribs.descr)) + end + + -- Write any pre-member annotation + output:write(get_annotation(attribs)) + + if (attribs.ctype == "STRING") then + -- TODO: Deal with array of strings.. + assert(count == 1); + write_field(output, "char", attribs.name, byte_size) + elseif (attribs.ctype == "UINT") then + if (byte_size == 1) then + write_field(output, "octet", attribs.name, count) + elseif (byte_size == 2) then + write_field(output, "unsigned short", attribs.name, count) + elseif (byte_size == 4) then + write_field(output, "unsigned long", attribs.name, count) + elseif (byte_size == 8) then + write_field(output, "unsigned long long", attribs.name, count) + else + error("Unsupported UINT byte length") + end + elseif (attribs.ctype == "INT") then + if (byte_size == 1) then + write_field(output, "char", attribs.name, count) + elseif (byte_size == 2) then + write_field(output, "short", attribs.name, count) + elseif (byte_size == 4) then + write_field(output, "long", attribs.name, count) + elseif (byte_size == 8) then + write_field(output, "long long", attribs.name, count) + else + error("Unsupported UINT byte length") + end + elseif (attribs.ctype == "FLOAT") then + if (byte_size == 4) then + write_field(output, "float", attribs.name, count) + elseif (byte_size == 8) then + write_field(output, "double", attribs.name, count) + else + error("Unsupported FLOAT byte length") + end + elseif (attribs.ctype == "BOOL") then + write_field(output, "boolean", attribs.name, count) + else + -- Assume it's a custom container + write_field(output, attribs.ctype, attribs.name, count) + end + + output:add_whitespace(1) +end + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: Invoke the line_writer function for the given entry +-- -- This can be CMD or TLM depending on the value of line_writer +-- -- ------------------------------------------------------------------------- +write_idl_lineitem = function(output, line_writer, entry, qual_prefix, descr, count, custom_containers) + local attribs = {} + + attribs.name = qual_prefix and SEDS.to_safe_identifier(qual_prefix) + -- Note that "resolved_size" only exists within nodes that the tool has calculated a size + -- If the size was specified directly in the XML as a "sizeinbits" attribute, then it appears + -- at the top level. + if (entry.resolved_size) then + attribs.bitsize = entry.resolved_size.bits + elseif (entry.sizeinbits) then + attribs.bitsize = entry.sizeinbits + else + attribs.bitsize = 0 + end + + if (not descr) then + descr = entry.attributes.shortdescription or "Value" + end + + -- The description should not have newlines or other big chunks of whitespace in it + descr = descr:gsub("[\t\n ]+", " ") + descr = descr:gsub("\"", "\'") + attribs.descr = descr + + -- if the type is an alias it must be followed to get back to the real type + local ref_entity = entry + while (ref_entity.entity_type == "ALIAS_DATATYPE") do + ref_entity = ref_entity.type + end + + if (ref_entity.entity_type == "CONTAINER_PADDING_ENTRY") then + -- For container padding entries, this can be put into the idl file as a series + -- of 8 bit integers. However this needs to come up with a unique name for each one. + local basename = attribs.name + local bits_remainder = attribs.bitsize + attribs.ctype = "UINT" + attribs.bitsize = 8 + while (true) do + attribs.name = basename .. "_B" .. bits_remainder + if (bits_remainder <= attribs.bitsize) then + break + end + line_writer(output, attribs) + bits_remainder = bits_remainder - attribs.bitsize + end + attribs.bitsize = bits_remainder + elseif (ref_entity.entity_type == "STRING_DATATYPE" or ref_entity.entity_type == "BINARY_DATATYPE") then + attribs.ctype = "STRING" + elseif (ref_entity.entity_type == "INTEGER_DATATYPE" or ref_entity.entity_type == "ENUMERATION_DATATYPE") then + attribs.ctype = (entry.is_signed and "INT") or "UINT" + elseif (ref_entity.entity_type == "FLOAT_DATATYPE") then + attribs.ctype = "FLOAT" + elseif (ref_entity.entity_type == "BOOLEAN_DATATYPE") then + attribs.ctype = "BOOL" + elseif (ref_entity.entity_type == "CONTAINER_DATATYPE") then + -- Write the container name - we'll define it as a separate struct elsewhere + attribs.ctype = entry.name + else + error(string.format("Unknown entity type %s for %s", entry, attribs.name)) + end + + -- Set min/max ranges if appropriate + if (attribs.ctype == "INT" or attribs.ctype == "UINT" or attribs.ctype == "FLOAT") then + if (entry.resolved_range) then + if (entry.resolved_range.min) then + attribs.min = entry.resolved_range.min.value + if (not entry.resolved_range.min.inclusive) then + attribs.min = attribs.min + 1 + end + end + if (entry.resolved_range.max) then + attribs.max = entry.resolved_range.max.value + if (not entry.resolved_range.max.inclusive) then + attribs.max = attribs.max - 1 + end + end + end + end + + line_writer(output, attribs, count) +end + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: Convert the given entry to a block of line items +-- -- This can be CMD or TLM depending on the value of line_writer +-- -- ------------------------------------------------------------------------- +write_idl_block = function(output, line_writer, entry, qual_prefix, descr, custom_containers) + if (entry.entity_type == "ARRAY_DATATYPE") then + write_idl_array_members(output, line_writer, entry, qual_prefix, descr, custom_containers) + elseif (entry.name) then + write_idl_lineitem(output, line_writer, entry, qual_prefix, descr, nil, custom_containers) + end +end + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: write a flattened array, adding a digit to the name prefix +-- -- ------------------------------------------------------------------------- +write_idl_array_members = function(output, line_writer, arr, qual_prefix, descr, custom_containers) + local count + + for dim in arr:iterate_subtree("DIMENSION") do + assert(dim.total_elements ~= nil and dim.total_elements > 0, "Array with bad count") + count = dim.total_elements + end + + -- Get the underlying data type + arr = arr.datatyperef + + write_idl_lineitem(output, line_writer, arr, qual_prefix, descr, count, custom_containers) +end + + +-- -- ------------------------------------------------------------------------- +-- -- Helper function: write a flattened container, adding member name to the prefix +-- -- ------------------------------------------------------------------------- +write_idl_container_members = function(output, line_writer, cont, qual_prefix, custom_containers) + local num_spares = 0 + for _, ntype, refnode in cont:iterate_members() do + -- By checking refnode this includes only direct entries, not basetypes + if (ntype) then + write_idl_block(output, line_writer, ntype, append_qual_prefix(qual_prefix, refnode and refnode.name), + refnode and refnode.attributes.shortdescription, custom_containers) + elseif (refnode and refnode.entity_type == "CONTAINER_PADDING_ENTRY") then + -- Padding entries do not have a type, but need to be put into the COSMOS DB + -- nonetheless, and they also need a unqique name. + num_spares = 1 + num_spares + write_idl_lineitem(output, line_writer, refnode, append_qual_prefix(qual_prefix, "spare" .. num_spares), + "Spare bits for padding") + end + end +end + +write_struct = function(output, container, qual_prefix) + local basetype = container.basetype + local inherit_string = "" + if basetype then + inherit_string = " : " .. basetype.name + end + + output:write(string.format("struct %s%s", container.name, inherit_string)) + output:start_group("{") + write_idl_container_members(output, write_idl_field, container, qual_prefix, nil) + output:end_group("};") +end + +local get_namespace = function(entry) + local full_qual = entry:get_qualified_name() + local namespace, _ = string.match(full_qual, "([^/]+)/([^/]+)") + return namespace +end + +local add_struct = function(node, field, cmd_code) + local structs = node.structs + if structs[field.name] == nil then + field.cmd_code = cmd_code + structs[field.name] = field + node.num_structs = node.num_structs + 1 + node.struct_order[field.name] = node.num_structs + end +end + +local get_underlying_field = function(field) + while field.entity_type == "ARRAY_DATATYPE" do + field = field.datatyperef + end + + return field +end + +get_includes = function(name, msg, namespace, file_node, all_files, is_root) + -- Extract the actual data type from the array + msg = get_underlying_field(msg) + + if (msg.entity_type == "CONTAINER_DATATYPE") then + for _, field, _ in msg:iterate_members() do + if (field ~= nil and field.name ~= "TelemetryHeader" and field.name ~= "CommandHeader") then + field = get_underlying_field(field) + + if (field.entity_type == "CONTAINER_DATATYPE") then + local field_namespace = get_namespace(field) + local new_file_name + local new_file_node + if is_root then + -- This is a direct command or tlm file, write it here + new_file_name = file_node.file_name + new_file_node = file_node + else + -- Definition is in a different namespace, put it in the appropriate file + new_file_name = SEDS.to_filename(string.format("%s.idl", field_namespace), "cfe_") + new_file_node = get_file_node(new_file_name, all_files) + + -- Since this is a different file, it needs to be included from the original + if file_node.include_file_names[new_file_name] == nil and file_node.file_name ~= new_file_name then + file_node.include_file_names[new_file_name] = true + end + end + + -- Store the struct info within this file + add_struct(new_file_node, field, nil) + + -- Recursively look to see if the member is itself a container with its own fields + get_includes(nil, field, namespace, new_file_node, all_files, false) + end + end + end + end +end + +local parse_cmds = function(cmd, module_node, all_files) + module_node.include_file_names["header.idl"] = true + for _, cc in ipairs(cmd.cc_list) do + local argtype = cc.argtype + add_struct(module_node, argtype, cc.value) + get_includes(argtype.name, argtype, get_namespace(argtype), module_node, all_files, true) + end +end + +local parse_tlms = function(argtype, module_node, all_files) + module_node.include_file_names["header.idl"] = true + add_struct(module_node, argtype, nil) + get_includes(argtype.name, argtype, get_namespace(argtype), module_node, all_files, true) +end + +local copy_headers = function() + -- Get the script path for relative pathing + local script_path = debug.getinfo(1, "S").source:sub(2) + local script_dir = script_path:match("(.*/)") or "./" + + -- Read the source + local infile = io.open(script_dir .. "../idl/header.idl", "rb") + local data = infile:read("*a") + infile:close() + + -- Use io not SEDS.output_open because the latter breaks the file + local output = io.open("idl/header.idl", "wb") + output:write(data) + output:close() +end + +local write_module_header = function(output, msg_id, module_name) + output:add_whitespace(1) + output:write(string.format("@ccsds_msg(id=%u)", msg_id)) + output:write(string.format("module %s", module_name)) +end + +function get_file_node(file_name, all_files) + return get_module_node(file_name, nil, nil, nil, all_files) +end + +function get_module_node(file_name, msg_id, module_name, is_cmd, all_files) + if all_files[file_name] == nil then + all_files[file_name] = { + file_name = file_name, + include_file_names = {}, + structs = {}, + struct_order = {}, + num_structs = 0, + msg_id = msg_id, + module_name = module_name, + is_cmd = is_cmd + } + end + + return all_files[file_name] +end + +function sort_structs(structs) + -- Within a file, structs need to be ordered by dependency such that any struct used by another comes first + local visited = {} + local sorted = {} + + local function dfs(node) + if visited[node.name] then + return true + end + + visited[node.name] = true + + node = get_underlying_field(node) + + if node.entity_type == "CONTAINER_DATATYPE" then + for _, member in node:iterate_members() do + if member ~= nil then + member = get_underlying_field(member) + end + + if member ~= nil and member.entity_type == "CONTAINER_DATATYPE" then + if not dfs(member) then + return false + end + end + end + end + + -- Only add structs that were in the original list to add + if structs[node.name] ~= nil then + table.insert(sorted, node) + end + return true + end + + for _, field in pairs(structs) do + if not dfs(field) then + return nil + end + end + + return sorted +end + +-- ------------------------- +-- MAIN ROUTINE +-- ------------------------- + +SEDS.output_mkdir("idl") +copy_headers() + +local all_files = {} + +for _, instance in ipairs(SEDS.highlevel_interfaces) do + local ds = instance.component:find_parent(SEDS.basenode_filter) + + -- The various interfaces should be attached under required_links + for _, binding in ipairs(instance.required_links) do + local reqintf = binding.reqintf + local cmd = reqintf.intf_commands and reqintf.intf_commands[1] + + -- SB interfaces should only have one "command" of the indication type (i.e. message appears on the bus) + -- Note "command" here is the EDS terminology, not CFE - the CFE intf is referred to as "telecommand" + -- The indication has one argument, which is the data associated with the message + if (cmd and cmd.refnode and cmd.refnode.name == "indication") then + local sb_params = binding.provinst:find_param_of_type("CFE_SB/SoftwareBusRouting") + local argtype = cmd.args[1].type + + if (sb_params) then + local file_name = SEDS.to_filename(reqintf.name .. ".idl", ds.name) + local msgid = sb_params.PubSub.MsgId -- This is the actual hex msgid value + + if (reqintf.type.name == "Telecommand") then + if (cmd.cc_list) then + local module_node = get_module_node(file_name, msgid.Value(), ds.name, true, all_files) + parse_cmds(cmd, module_node, all_files) + end + elseif (reqintf.type.name == "Telemetry") then + local module_node = get_module_node(file_name, msgid.Value(), ds.name, false, all_files) + parse_tlms(argtype, module_node, all_files) + end + end + end + end +end + +for file_name, file_data in pairs(all_files) do + local file_path = "idl/" .. file_name + local output = SEDS.output_open(file_path) + + for include_file_name, _ in pairs(file_data.include_file_names) do + output:write("#include \"" .. include_file_name .. "\"") + end + + output:add_whitespace(1) + + -- This is a cmd or tlm definition with a message ID + if file_data.module_name ~= nil then + if file_data.is_cmd then + write_module_header(output, file_data.msg_id, string.format("%s_cmd", file_data.module_name)) + else + write_module_header(output, file_data.msg_id, string.format("%s_tlm", file_data.module_name)) + end + output:start_group("{") + end + + -- Structs in an IDL file must be ordered such that they are defined before they are referenced + -- Sort them by that property + local structs = sort_structs(file_data.structs) + assert(structs ~= nil, "Failed to sort structs") + + for _, struct in pairs(structs) do + if struct.cmd_code ~= nil then + output:write(string.format("@command(fn_code=%u)", struct.cmd_code)) + end + write_struct(output, struct, nil) + output:add_whitespace(1) + end + + if file_data.module_name ~= nil then + output:end_group("};") + end + + SEDS.output_close(output) +end diff --git a/cfecfs/missionlib/eds/90-write_ccdd_json.lua b/cfecfs/missionlib/eds/90-write_ccdd_json.lua index c054ff1..ddbf1c5 100644 --- a/cfecfs/missionlib/eds/90-write_ccdd_json.lua +++ b/cfecfs/missionlib/eds/90-write_ccdd_json.lua @@ -168,54 +168,58 @@ for _,instance in ipairs(SEDS.highlevel_interfaces) do -- The various interfaces should be attached under required_links for _,binding in ipairs(instance.required_links) do local reqintf = binding.reqintf - local cmd = reqintf.intf_commands and reqintf.intf_commands[1] - - -- SB interfaces should only have one "command" of the indication type (i.e. message appears on the bus) - -- Note "command" here is the EDS terminology, not CFE - the CFE intf is referred to as "telecommand" - -- The indication has one argument, which is the data associated with the message - if (cmd.refnode.name == "indication") then - local sb_params = find_param_of_type(binding.provinst,"CFE_SB/SoftwareBusRouting") - local argtype = cmd.args[1].type - local prefix - - if (reqintf.type.name == "Telecommand") then - prefix = "cmd_" - elseif (reqintf.type.name == "Telemetry") then - prefix = "tlm_" - else - prefix = "" - end - - if (sb_params) then - local output = SEDS.output_open("json/" .. SEDS.to_filename(reqintf.name .. "_interface.json", ds.name)); - local msgid - - -- For now, just make up a symbolic name according to typical CFE patterns - msgid = SEDS.to_macro_name(ds.name .. "_" .. reqintf.name .. "_MID") - --msgid = sb_params.PubSub.MsgId -- This would be the actual hex msgid value + local intf_type_str = reqintf.type:get_qualified_name() + if (intf_type_str == "CFE_SB/Telemetry" or intf_type_str == "CFE_SB/Telecommand") then + local cmd = reqintf.intf_commands and reqintf.intf_commands[1] + + -- SB interfaces should only have one "command" of the indication type (i.e. message appears on the bus) + -- Note "command" here is the EDS terminology, not CFE - the CFE intf is referred to as "telecommand" + -- The indication has one argument, which is the data associated with the message + if (cmd.refnode.name == "indication") then + local sb_params = find_param_of_type(binding.provinst,"CFE_SB/SoftwareBusRouting") + local argtype = cmd.args[1].type + local prefix + + if (reqintf.type.name == "Telecommand") then + prefix = "cmd_" + elseif (reqintf.type.name == "Telemetry") then + prefix = "tlm_" + else + prefix = "" + end - output:start_group("{") - output:write(string.format("\"%smid_name\": \"%s\",", prefix, msgid)) - output:write(string.format("\"%sdescription\": \"%s\",", prefix, reqintf.attributes.shortdescription or "")) - if (cmd.cc_list) then - output:start_group("\"cmd_codes\": [") - for _,cc in ipairs(cmd.cc_list) do - output:append_previous(",") - output:start_group("{") - write_cc_json_parameters(output, cc) - output:end_group("}") + if (sb_params) then + local output = SEDS.output_open("json/" .. SEDS.to_filename(reqintf.name .. "_interface.json", ds.name)); + local msgid + + -- For now, just make up a symbolic name according to typical CFE patterns + msgid = SEDS.to_macro_name(ds.name .. "_" .. reqintf.name .. "_MID") + --msgid = sb_params.PubSub.MsgId -- This would be the actual hex msgid value + + output:start_group("{") + output:write(string.format("\"%smid_name\": \"%s\",", prefix, msgid)) + output:write(string.format("\"%sdescription\": \"%s\",", prefix, reqintf.attributes.shortdescription or "")) + if (cmd.cc_list) then + output:start_group("\"cmd_codes\": [") + for _,cc in ipairs(cmd.cc_list) do + output:append_previous(",") + output:start_group("{") + write_cc_json_parameters(output, cc) + output:end_group("}") + end + output:end_group("]") + else + local ctype = argtype.header_data and argtype.header_data.typedef_name or argtype:get_flattened_name() + output:write(string.format("\"%sdata_type\": \"%s\",", prefix, ctype)) + output:start_group(string.format("\"%sparameters\": [", prefix, ctype)) + write_json_container_members(output, argtype) + output:end_group("]") end - output:end_group("]") - else - local ctype = argtype.header_data and argtype.header_data.typedef_name or argtype:get_flattened_name() - output:write(string.format("\"%sdata_type\": \"%s\",", prefix, ctype)) - output:start_group(string.format("\"%sparameters\": [", prefix, ctype)) - write_json_container_members(output, argtype) - output:end_group("]") + output:end_group("}") + + SEDS.output_close(output) end - output:end_group("}") - SEDS.output_close(output) end end diff --git a/cfecfs/missionlib/eds/91-write_cosmos_txt.lua b/cfecfs/missionlib/eds/91-write_cosmos_txt.lua index b846a4c..b27b58a 100644 --- a/cfecfs/missionlib/eds/91-write_cosmos_txt.lua +++ b/cfecfs/missionlib/eds/91-write_cosmos_txt.lua @@ -33,7 +33,7 @@ else ccsds_append = "" -- This must _not_ put a redundant BIG_ENDIAN flag on the ccsds header, it confuses cosmos end -local global_fsw_title = SEDS.to_macro_name(SEDS.get_define("MISSION_NAME") or "fsw") +local global_fsw_title = SEDS.to_macro_name(SEDS.get_define("EDSTOOL_PROJECT_NAME") or "fsw") -- ------------------------------------------------------------------------- -- Helper function: assemble the combined element name @@ -355,35 +355,38 @@ for _,instance in ipairs(SEDS.highlevel_interfaces) do -- The various interfaces should be attached under required_links for _,binding in ipairs(instance.required_links) do local reqintf = binding.reqintf - local cmd = reqintf.intf_commands and reqintf.intf_commands[1] - - -- SB interfaces should only have one "command" of the indication type (i.e. message appears on the bus) - -- Note "command" here is the EDS terminology, not CFE - the CFE intf is referred to as "telecommand" - -- The indication has one argument, which is the data associated with the message - if (cmd.refnode.name == "indication") then - local sb_params = binding.provinst:find_param_of_type("CFE_SB/SoftwareBusRouting") - local argtype = cmd.args[1].type - - if (sb_params) then - local file = "cosmos/" .. SEDS.to_filename(reqintf.name .. "_cosmos_intf.txt", ds.name) - local msgid = sb_params.PubSub.MsgId -- This is the actual hex msgid value - local output - - if (reqintf.type.name == "Telecommand") then - if (cmd.cc_list) then - output = SEDS.output_open(file) - for _,cc in ipairs(cmd.cc_list) do - write_cmd_intf_params(output,ds,reqintf,msgid,cc) - output:add_whitespace(1) + local intf_type_str = reqintf.type:get_qualified_name() + if (intf_type_str == "CFE_SB/Telemetry" or intf_type_str == "CFE_SB/Telecommand") then + local cmd = reqintf.intf_commands and reqintf.intf_commands[1] + + -- SB interfaces should only have one "command" of the indication type (i.e. message appears on the bus) + -- Note "command" here is the EDS terminology, not CFE - the CFE intf is referred to as "telecommand" + -- The indication has one argument, which is the data associated with the message + if (cmd.refnode.name == "indication") then + local sb_params = binding.provinst:find_param_of_type("CFE_SB/SoftwareBusRouting") + local argtype = cmd.args[1].type + + if (sb_params) then + local file = "cosmos/" .. SEDS.to_filename(reqintf.name .. "_cosmos_intf.txt", ds.name) + local msgid = sb_params.PubSub.MsgId -- This is the actual hex msgid value + local output + + if (reqintf.type.name == "Telecommand") then + if (cmd.cc_list) then + output = SEDS.output_open(file) + for _,cc in ipairs(cmd.cc_list) do + write_cmd_intf_params(output,ds,reqintf,msgid,cc) + output:add_whitespace(1) + end + SEDS.output_close(output) end + elseif (reqintf.type.name == "Telemetry") then + output = SEDS.output_open(file) + write_tlm_intf_items(output,ds,reqintf,msgid,argtype) SEDS.output_close(output) end - elseif (reqintf.type.name == "Telemetry") then - output = SEDS.output_open(file) - write_tlm_intf_items(output,ds,reqintf,msgid,argtype) - SEDS.output_close(output) - end + end end end end diff --git a/cfecfs/missionlib/eds/instance-rules.xml b/cfecfs/missionlib/eds/instance-rules.xml index fcc7ac2..18c3378 100644 --- a/cfecfs/missionlib/eds/instance-rules.xml +++ b/cfecfs/missionlib/eds/instance-rules.xml @@ -30,6 +30,8 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/edslib/unit-test/edslib_basic_test.c b/edslib/unit-test/edslib_basic_test.c deleted file mode 100644 index 1fa6d9b..0000000 --- a/edslib/unit-test/edslib_basic_test.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation - * - * Copyright (c) 2020 United States Government as represented by - * the Administrator of the National Aeronautics and Space Administration. - * All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/** - * \file edslib_basic_test.c - * \ingroup edslib - * \author joseph.p.hickey@nasa.gov - * - * Basic testing of the "data type" EDS structures - */ - -#include -#include -#include - -#include "utassert.h" - -#include -#include -#include - -#include "edslib_datatypedb.h" -#include "edslib_id.h" -#include "UTHDR_msgdefs.h" -#include "UT1_msgdefs.h" - -extern EdsLib_DataDictionary_t UT1_DATATYPE_DB; -extern EdsLib_DataDictionary_t UTHDR_DATATYPE_DB; -extern EdsLib_DataDictionary_t ut_base_types_DATATYPE_DB; - -EdsLib_DataDictionary_t *UTM_EDS_LOCAL_APPTBL[UTM_EDS_MAX_INDEX] = { NULL }; -EdsLib_DatabaseObject_t GD_BASIC = -{ - .AppTableSize = UTM_EDS_MAX_INDEX, - .AppDataTable = UTM_EDS_LOCAL_APPTBL, - .AppNameTable = NULL, - .SymbolTable = NULL -}; - -typedef struct -{ - UTHDR_UtPriHdr_t Pri; - UTHDR_UtTlmSecHdr_t Tlm; -} TestTlmHdr_t; - -typedef struct -{ - UTHDR_UtPriHdr_t Pri; - UTHDR_UtCmdSecHdr_t Cmd; -} TestCmdHdr_t; - -typedef struct -{ - TestTlmHdr_t Hdr; - UT1_Tlm1_t Payload; -} TestTlm_t; - -typedef struct -{ - TestCmdHdr_t Hdr; - UT1_Cmd_t Payload; -} TestCmd_t; - -union -{ - uint8_t Bytes[1]; - TestCmd_t Cmd; - TestTlm_t Tlm; -} Buffer; - -typedef struct -{ - bool DoDescend; - bool DoStop; - const char *TestName; - const char **CurrentLine; -} WalkTestState_t; - -const char *BasicFormats_EXPECTED[] = -{ - "BasicFormats:1:I=1/T=1/O=0/S=1/N=0", - "BasicFormats:1:I=2/T=2/O=0/S=1/N=0", - "BasicFormats:1:I=3/T=1/O=0/S=2/N=0", - "BasicFormats:1:I=4/T=2/O=0/S=2/N=0", - "BasicFormats:1:I=5/T=1/O=0/S=4/N=0", - "BasicFormats:1:I=6/T=2/O=0/S=4/N=0", - "BasicFormats:1:I=7/T=1/O=0/S=8/N=0", - "BasicFormats:1:I=8/T=2/O=0/S=8/N=0", - "BasicFormats:1:I=9/T=2/O=0/S=8/N=0", - "BasicFormats:1:I=10/T=3/O=0/S=4/N=0", - "BasicFormats:1:I=11/T=3/O=0/S=8/N=0", - "BasicFormats:1:I=12/T=7/O=0/S=4/N=1", - "BasicFormats:1:I=13/T=7/O=0/S=8/N=2", - "BasicFormats:1:I=14/T=7/O=0/S=8/N=2", - "BasicFormats:1:I=15/T=5/O=0/S=4/N=2", - "BasicFormats:1:I=16/T=5/O=0/S=4/N=2", - "BasicFormats:1:I=17/T=5/O=0/S=4/N=2", - "BasicFormats:1:I=18/T=7/O=0/S=8/N=1", - "BasicFormats:1:I=19/T=7/O=0/S=312/N=2", - "BasicFormats:1:I=20/T=7/O=0/S=12/N=2", - "BasicFormats:1:I=21/T=7/O=0/S=12/N=2", - "BasicFormats:1:I=22/T=2/O=0/S=1/N=0", - "BasicFormats:1:I=23/T=4/O=0/S=10/N=0", - "BasicFormats:1:I=24/T=6/O=0/S=4/N=4", - "BasicFormats:1:I=25/T=6/O=0/S=250/N=250", - "BasicFormats:1:I=26/T=6/O=0/S=3/N=3", - "BasicFormats:1:I=27/T=6/O=0/S=6/N=3", - "BasicFormats:1:I=28/T=6/O=0/S=4/N=1", - "BasicFormats:1:I=29/T=5/O=0/S=48/N=10", - "BasicFormats:1:I=30/T=6/O=0/S=240/N=5", - "BasicFormats:1:I=31/T=5/O=0/S=4/N=1", - "BasicFormats:1:I=32/T=5/O=0/S=304/N=5", - "BasicFormats:1:I=33/T=5/O=0/S=250/N=1", - NULL -}; - -const char *PayloadShallow_EXPECTED[] = -{ - "PayloadShallow:2:I=0/T=7/O=0/S=312/N=2", - "PayloadShallow:1:I=0/T=7/O=0/S=8/N=2", - "PayloadShallow:1:I=1/T=5/O=8/S=304/N=5", - "PayloadShallow:3:I=0/T=7/O=0/S=312/N=2", - NULL -}; - -const char *PayloadDeep_EXPECTED[] = -{ - "PayloadDeep:2:I=0/T=7/O=0/S=312/N=2", - "PayloadDeep:1:I=0/T=7/O=0/S=8/N=2", - "PayloadDeep:2:I=1/T=7/O=0/S=8/N=2", - "PayloadDeep:1:I=0/T=7/O=0/S=4/N=1", - "PayloadDeep:2:I=1/T=7/O=0/S=4/N=1", - "PayloadDeep:1:I=0/T=5/O=0/S=4/N=2", - "PayloadDeep:2:I=1/T=5/O=0/S=4/N=2", - "PayloadDeep:1:I=0/T=2/O=0/S=2/N=0", - "PayloadDeep:1:I=1/T=2/O=2/S=2/N=0", - "PayloadDeep:3:I=1/T=5/O=0/S=4/N=2", - "PayloadDeep:3:I=1/T=7/O=0/S=4/N=1", - "PayloadDeep:1:I=1/T=5/O=4/S=4/N=2", - "PayloadDeep:2:I=2/T=5/O=4/S=4/N=2", - "PayloadDeep:1:I=0/T=2/O=4/S=2/N=0", - "PayloadDeep:1:I=1/T=2/O=6/S=2/N=0", - "PayloadDeep:3:I=2/T=5/O=4/S=4/N=2", - "PayloadDeep:3:I=1/T=7/O=0/S=8/N=2", - "PayloadDeep:1:I=1/T=5/O=8/S=304/N=5", - "PayloadDeep:2:I=2/T=5/O=8/S=304/N=5", - "PayloadDeep:1:I=0/T=5/O=8/S=48/N=10", - "PayloadDeep:2:I=1/T=5/O=8/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=8/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=9/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=10/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=12/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=14/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=16/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=20/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=24/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=32/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=40/S=10/N=0", - "PayloadDeep:3:I=1/T=5/O=8/S=48/N=10", - "PayloadDeep:1:I=1/T=6/O=56/S=3/N=3", - "PayloadDeep:2:I=2/T=6/O=56/S=3/N=3", - "PayloadDeep:1:I=0/T=2/O=56/S=1/N=0", - "PayloadDeep:1:I=1/T=2/O=57/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=58/S=1/N=0", - "PayloadDeep:3:I=2/T=6/O=56/S=3/N=3", - "PayloadDeep:1:I=2/T=6/O=60/S=6/N=3", - "PayloadDeep:2:I=3/T=6/O=60/S=6/N=3", - "PayloadDeep:1:I=0/T=2/O=60/S=2/N=0", - "PayloadDeep:1:I=1/T=2/O=62/S=2/N=0", - "PayloadDeep:1:I=2/T=2/O=64/S=2/N=0", - "PayloadDeep:3:I=3/T=6/O=60/S=6/N=3", - "PayloadDeep:1:I=3/T=6/O=68/S=4/N=1", - "PayloadDeep:2:I=4/T=6/O=68/S=4/N=1", - "PayloadDeep:1:I=0/T=2/O=68/S=4/N=0", - "PayloadDeep:3:I=4/T=6/O=68/S=4/N=1", - "PayloadDeep:1:I=4/T=6/O=72/S=240/N=5", - "PayloadDeep:2:I=5/T=6/O=72/S=240/N=5", - "PayloadDeep:1:I=0/T=5/O=72/S=48/N=10", - "PayloadDeep:2:I=1/T=5/O=72/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=72/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=73/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=74/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=76/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=78/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=80/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=84/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=88/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=96/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=104/S=10/N=0", - "PayloadDeep:3:I=1/T=5/O=72/S=48/N=10", - "PayloadDeep:1:I=1/T=5/O=120/S=48/N=10", - "PayloadDeep:2:I=2/T=5/O=120/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=120/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=121/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=122/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=124/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=126/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=128/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=132/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=136/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=144/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=152/S=10/N=0", - "PayloadDeep:3:I=2/T=5/O=120/S=48/N=10", - "PayloadDeep:1:I=2/T=5/O=168/S=48/N=10", - "PayloadDeep:2:I=3/T=5/O=168/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=168/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=169/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=170/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=172/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=174/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=176/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=180/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=184/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=192/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=200/S=10/N=0", - "PayloadDeep:3:I=3/T=5/O=168/S=48/N=10", - "PayloadDeep:1:I=3/T=5/O=216/S=48/N=10", - "PayloadDeep:2:I=4/T=5/O=216/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=216/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=217/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=218/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=220/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=222/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=224/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=228/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=232/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=240/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=248/S=10/N=0", - "PayloadDeep:3:I=4/T=5/O=216/S=48/N=10", - "PayloadDeep:1:I=4/T=5/O=264/S=48/N=10", - "PayloadDeep:2:I=5/T=5/O=264/S=48/N=10", - "PayloadDeep:1:I=0/T=2/O=264/S=1/N=0", - "PayloadDeep:1:I=1/T=1/O=265/S=1/N=0", - "PayloadDeep:1:I=2/T=2/O=266/S=1/N=0", - "PayloadDeep:1:I=3/T=2/O=268/S=2/N=0", - "PayloadDeep:1:I=4/T=1/O=270/S=2/N=0", - "PayloadDeep:1:I=5/T=2/O=272/S=4/N=0", - "PayloadDeep:1:I=6/T=1/O=276/S=4/N=0", - "PayloadDeep:1:I=7/T=3/O=280/S=4/N=0", - "PayloadDeep:1:I=8/T=3/O=288/S=8/N=0", - "PayloadDeep:1:I=9/T=4/O=296/S=10/N=0", - "PayloadDeep:3:I=5/T=5/O=264/S=48/N=10", - "PayloadDeep:3:I=5/T=6/O=72/S=240/N=5", - "PayloadDeep:3:I=2/T=5/O=8/S=304/N=5", - "PayloadDeep:3:I=0/T=7/O=0/S=312/N=2", - NULL -}; - -const char *PayloadStop_EXPECTED[] = -{ - "PayloadStop:2:I=0/T=7/O=0/S=312/N=2", - NULL -}; - -const char *Invalid_EXPECTED[] = -{ - "INVALID", - NULL -}; - -static EdsLib_Iterator_Rc_t BasicCheckCallback(EdsLib_Iterator_CbType_t CbType, void *Arg, EdsLib_Basic_MemberInfo_t *WalkState) -{ - char OutputBuffer[128]; - const char *TempPtr; - WalkTestState_t *TestState = Arg; - - snprintf(OutputBuffer, sizeof(OutputBuffer), "%s:%u:I=%u/T=%u/O=%u/S=%u/N=%u", - TestState->TestName, - (unsigned int)CbType, - (unsigned int)WalkState->ComponentInfo.CurrIndex, - (unsigned int)WalkState->TypeInfo.ElemType, - (unsigned int)WalkState->ComponentInfo.AbsOffset, - (unsigned int)WalkState->TypeInfo.Size.Bytes, - (unsigned int)WalkState->TypeInfo.NumSubElements); - - TempPtr = *TestState->CurrentLine; - if (TempPtr == NULL) - { - TempPtr = "{END}"; - } - else - { - ++TestState->CurrentLine; - } - - UtAssert_True(strcmp(TempPtr, OutputBuffer) == 0, "%s", OutputBuffer); - - if (TestState->DoStop) - { - return EDSLIB_ITERATOR_RC_STOP; - } - - if (TestState->DoDescend && CbType == EDSLIB_ITERATOR_CBTYPE_MEMBER) - { - return EDSLIB_ITERATOR_RC_DESCEND; - } - - return EDSLIB_ITERATOR_RC_CONTINUE; -} - -void EdsLib_Basic_Test(void) -{ - EdsLib_Id_t MsgId; - int32_t rc; - uint8_t *BufPtr; - uint16_t i; - uint16_t Orig16; - uint32_t Orig32; - WalkTestState_t WalkTestState; - EdsLib_DataTypeDB_TypeInfo_t TypeInfo; - EdsLib_DataTypeDB_EntityInfo_t CompInfo; - - rc = EdsLib_DataTypeDB_Register(&GD_BASIC, &UT1_DATATYPE_DB, &i); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(i == UTM_EDS_UT1_INDEX); - rc = EdsLib_DataTypeDB_Register(&GD_BASIC, &UTHDR_DATATYPE_DB, &i); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(i == UTM_EDS_UTHDR_INDEX); - rc = EdsLib_DataTypeDB_Register(&GD_BASIC, &ut_base_types_DATATYPE_DB, &i); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(i == UTM_EDS_ut_base_types_INDEX); - - memset(&WalkTestState, 0, sizeof(WalkTestState)); - WalkTestState.TestName = "BasicFormats"; - WalkTestState.CurrentLine = BasicFormats_EXPECTED; - EdsLib_Iterator_Formats(&GD_BASIC, BasicCheckCallback, &WalkTestState); - UtAssert_Simple(*WalkTestState.CurrentLine == NULL); - - UtAssert_Simple(EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_BIG_ENDIAN, EDSLIB_BYTE_ORDER_LITTLE_ENDIAN)); - UtAssert_Simple(EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_NATIVE, EDSLIB_BYTE_ORDER_NON_NATIVE)); - if (EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_NATIVE, EDSLIB_BYTE_ORDER_BIG_ENDIAN) == 0) - { - UtAssert_Simple(EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_NATIVE, EDSLIB_BYTE_ORDER_LITTLE_ENDIAN)); - } - else - { - UtAssert_Simple(!EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_NATIVE, EDSLIB_BYTE_ORDER_LITTLE_ENDIAN)); - } - - BufPtr = Buffer.Bytes; - for (i=0; i < sizeof(Buffer); ++i) - { - BufPtr[i] = i & 0xFF; - } - Orig32 = Buffer.Tlm.Payload.bitmask32[0]; - Orig16 = Buffer.Tlm.Hdr.Pri.UtLength; - - EdsLib_SwapInPlace(&GD_BASIC, EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_BasePacket_Intf_t_DATADICTIONARY), Buffer.Bytes, 0); - UtAssert_True(Orig16 != Buffer.Tlm.Hdr.Pri.UtLength, "Orig16 (%x) != Buffer.Tlm.Hdr.Pri.UtLength(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Tlm.Hdr.Pri.UtLength); - UtAssert_True(Orig32 == Buffer.Tlm.Payload.bitmask32[0], "Orig32 (%x) == Buffer.Tlm.Payload.bitmask32[0](%x)", - (unsigned int)Orig32 , (unsigned int)Buffer.Tlm.Payload.bitmask32[0]); - EdsLib_ByteSwap((uint8_t*)&Orig16, sizeof(Orig16)); - UtAssert_True(Orig16 == Buffer.Tlm.Hdr.Pri.UtLength, "Orig16 (%x) == Buffer.Tlm.Hdr.Pri.UtLength(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Tlm.Hdr.Pri.UtLength); - - MsgId = EdsLib_Lookup_DerivedType(&GD_BASIC, EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_BasePacket_Intf_t_DATADICTIONARY), - 0, UT1_TestTLM_MSG); - Orig16 = Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1; - EdsLib_SwapInPlace(&GD_BASIC, MsgId, Buffer.Bytes, sizeof(UTHDR_UtPriHdr_t)); - UtAssert_True(Orig16 != Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1, "Orig16 (%x) != Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1); - UtAssert_True(Orig32 != Buffer.Tlm.Payload.bitmask32[0], "Orig32 (%x) != Buffer.Tlm.Payload.bitmask32[0](%x)", - (unsigned int)Orig32 , (unsigned int)Buffer.Tlm.Payload.bitmask32[0]); - EdsLib_ByteSwap((uint8_t*)&Orig16, sizeof(Orig16)); - EdsLib_ByteSwap((uint8_t*)&Orig32, sizeof(Orig32)); - UtAssert_True(Orig16 == Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1, "Orig16 (%x) == Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Tlm.Hdr.Tlm.TlmExtraInfo1); - UtAssert_True(Orig32 == Buffer.Tlm.Payload.bitmask32[0], "Orig32 (%x) == Buffer.Tlm.Payload.bitmask32[0](%x)", - (unsigned int)Orig32 , (unsigned int)Buffer.Tlm.Payload.bitmask32[0]); - - for (i=0; i < sizeof(Buffer); ++i) - { - BufPtr[i] = i & 0xFF; - } - - Orig16 = Buffer.Cmd.Hdr.Cmd.UtCommandId; - MsgId = EdsLib_Lookup_DerivedType(&GD_BASIC, EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_BasePacket_Intf_t_DATADICTIONARY), - 0, UT1_TestTLM_MSG); - EdsLib_SwapInPlace(&GD_BASIC, MsgId, Buffer.Bytes, 0); - UtAssert_True(Orig16 != Buffer.Cmd.Hdr.Cmd.UtCommandId, "Orig16 (%x) != Buffer.Cmd.Hdr.Cmd.UtCommandId(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Cmd.Hdr.Cmd.UtCommandId); - EdsLib_ByteSwap((uint8_t*)&Orig16, sizeof(Orig16)); - UtAssert_True(Orig16 == Buffer.Cmd.Hdr.Cmd.UtCommandId, "Orig16 (%x) == Buffer.Cmd.Hdr.Cmd.UtCommandId(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Cmd.Hdr.Cmd.UtCommandId); - - EdsLib_SwapInPlace(&GD_BASIC, EDSLIB_ID_INVALID, Buffer.Bytes, 0); - UtAssert_True(Orig16 == Buffer.Cmd.Hdr.Cmd.UtCommandId, "Orig16 (%x) == Buffer.Cmd.Hdr.Cmd.UtCommandId(%x)", - (unsigned int)Orig16 , (unsigned int)Buffer.Cmd.Hdr.Cmd.UtCommandId); - -#ifdef JPHFIX /* Auth levels not currently implemented in SOIS XML schema */ - TestResultU8 = EdsLib_Lookup_AuthLevel(&GD_BASIC, MsgId); - UtAssert_True(TestResultU8 == 200, "EdsLib_Lookup_AuthLevel(GD, %x) (%u) == 200", - (unsigned int)MsgId, (unsigned int)TestResultU8); - EdsLib_Set_CommandCode(&MsgId, UT1_Cmd2_CC); - TestResultU8 = EdsLib_Lookup_AuthLevel(&GD_BASIC, MsgId); - UtAssert_True(TestResultU8 == 100, "EdsLib_Lookup_AuthLevel(GD, %x) (%u) == 100", - (unsigned int)MsgId, (unsigned int)TestResultU8); -#endif - -#ifdef JPHFIX - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_TestCMD_Intf_t_DATADICTIONARY); - EdsLib_Set_CpuNumber(&MsgId, 3); - StreamId = EdsLib_MsgId_To_StreamId(&GD_BASIC, MsgId); - UtAssert_True(StreamId != 0, "StreamId (%x) != 0", (unsigned int)StreamId); - Orig32 = MsgId; - MsgId = EdsLib_MsgId_From_StreamId(&GD_BASIC, StreamId); - TestResultU16 = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(TestResultU16 == 3, "EdsLib_Get_CpuNumber(%x) (%u) == 3", - (unsigned int)MsgId, (unsigned int)TestResultU16); - TestResultU16 = EdsLib_Get_AppIdx(MsgId); - TestCompareU16 = EdsLib_Get_AppIdx(Orig32); - UtAssert_True(TestResultU16 == TestCompareU16, "EdsLib_Get_AppIdx(%x) (%u) == EdsLib_Get_AppIdx(%x) (%u)", - (unsigned int)MsgId, (unsigned int)TestResultU16, - (unsigned int)Orig32, (unsigned int)TestCompareU16); - TestResultU16 = EdsLib_Get_FormatIdx(MsgId); - TestCompareU16 = EdsLib_Get_FormatIdx(Orig32); - UtAssert_True(TestResultU16 == TestCompareU16, "EdsLib_Get_FormatIndex(%x) (%u) == EdsLib_Get_FormatIndex(%x) (%u)", - (unsigned int)MsgId, (unsigned int)TestResultU16, - (unsigned int)Orig32, (unsigned int)TestCompareU16); - MsgId = EdsLib_Lookup_DerivedType(&GD_BASIC, GD_BASIC.PrimaryHeaderMsgId, offsetof(UTHDR_UtPriHdr_t, UtStreamId), UT1_TestTLM_MSG); - TestResultU8 = EdsLib_Basic_Validate_MessageSize(&GD_BASIC, MsgId, sizeof(TestTlm_t)); - UtAssert_True(TestResultU8, "EdsLib_Basic_Validate_MessageSize(GD, %x, %u)", - (unsigned int)MsgId, (unsigned int)sizeof(TestTlm_t)); - TestResultU8 = EdsLib_Basic_Validate_MessageSize(&GD_BASIC, MsgId, sizeof(TestTlm_t) - 3); - UtAssert_True(!TestResultU8, "!EdsLib_Basic_Validate_MessageSize(GD, %x, %u)", - (unsigned int)MsgId, (unsigned int)(sizeof(TestTlm_t) - 3)); - EdsLib_Set_CpuNumber(&MsgId, 4); - StreamId = EdsLib_MsgId_To_StreamId(&GD_BASIC, MsgId); - UtAssert_True(StreamId != 0, "StreamId (%x) != 0", (unsigned int)StreamId ); - Orig32 = MsgId; - MsgId = EdsLib_MsgId_From_StreamId(&GD_BASIC, StreamId); - UtAssert_True(MsgId == Orig32, "MsgId (%x) == Orig32(%x)", (unsigned int)MsgId , (unsigned int)Orig32); -#endif - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_Table1_t_DATADICTIONARY); -#ifdef JPHFIX /* Auth levels not currently implemented in SOIS XML schema */ - TestResultU8 = EdsLib_Lookup_AuthLevel(&GD_BASIC, MsgId); - UtAssert_True(TestResultU8 == 0, "EdsLib_Lookup_AuthLevel(GD, %x) (%u) == 0", - (unsigned int)MsgId, (unsigned int)TestResultU8); -#endif - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(TypeInfo.Size.Bytes == sizeof(UT1_Table1_t), "EdsLib_DataTypeDB_GetTypeInfo(GD, %x) (size %u) == sizeof(UT1_Table1_t)(%u)", - (unsigned int)MsgId, (unsigned int)TypeInfo.Size.Bytes, (unsigned int)sizeof(UT1_Table1_t)); - EdsLib_Set_AppIdx(&MsgId, 30); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_True(rc != EDSLIB_SUCCESS, "EdsLib_DataTypeDB_GetTypeInfo(GD, %x) (%u) != EDSLIB_SUCCESS", - (unsigned int)MsgId, (unsigned int)rc); - rc = EdsLib_DataTypeDB_GetTypeInfo(NULL, MsgId, &TypeInfo); - UtAssert_True(rc != EDSLIB_SUCCESS, "EdsLib_DataTypeDB_GetTypeInfo(NULL, %x) (%u) != EDSLIB_SUCCESS", - (unsigned int)MsgId, (unsigned int)rc); - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, 1000); - rc = EdsLib_DataTypeDB_GetTypeInfo(NULL, MsgId, &TypeInfo); - UtAssert_True(rc != EDSLIB_SUCCESS, "EdsLib_DataTypeDB_GetTypeInfo(GD, %x) (%u) != EDSLIB_SUCCESS", - (unsigned int)MsgId, (unsigned int)rc); - - /* Test calls for dispatching messages (determining payload subtypes given the primary interface type) */ - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_TestTLM_Intf_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByOffset(&GD_BASIC, MsgId, sizeof(UTHDR_UtTlm_Intf_t), &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(CompInfo.CurrIndex == 1, "EdsLib_Lookup_SubMemberByOffset(%x) (%u) == 1", - (unsigned int)MsgId, (unsigned int)CompInfo.CurrIndex); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == UT1_Tlm1_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_Tlm1_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_5_Array_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByOffset(&GD_BASIC, MsgId, 1 + (2 * sizeof(UT1_BasicDataTypes_t)), &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(CompInfo.CurrIndex == 2, "EdsLib_Lookup_SubMemberByOffset(%x) (%u) == 2", - (unsigned int)MsgId, (unsigned int)CompInfo.CurrIndex); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == UT1_BasicDataTypes_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_BasicDataTypes_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByOffset(&GD_BASIC, MsgId, 5, &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(CompInfo.CurrIndex == 3, "EdsLib_Lookup_SubMemberByOffset(%x) (%u) == 3", - (unsigned int)MsgId, (unsigned int)CompInfo.CurrIndex); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_ut_base_types_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_ut_base_types_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == uint16_Atom_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == uint16_Atom_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_Cmd1_Intf_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByIndex(&GD_BASIC, MsgId, 1, &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(CompInfo.AbsOffset == sizeof(UTHDR_UtCmd_Intf_t), "EdsLib_Lookup_SubMemberByIndex(%x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)CompInfo.AbsOffset, (unsigned int)sizeof(UTHDR_UtCmd_Intf_t)); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == UT1_Cmd_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_Cmd_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_5_Array_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByIndex(&GD_BASIC, MsgId, 4, &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(CompInfo.AbsOffset == 4 * sizeof(UT1_BasicDataTypes_t), "EdsLib_Lookup_SubMemberByOffset(%x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)CompInfo.AbsOffset, 4 * (unsigned int)sizeof(UT1_BasicDataTypes_t)); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == UT1_BasicDataTypes_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_BasicDataTypes_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(TypeInfo.ElemType == EDSLIB_BASICTYPE_CONTAINER, "ElemType (%u) == EDSLIB_BASICTYPE_CONTAINER", (unsigned int)TypeInfo.ElemType); - UtAssert_True(TypeInfo.NumSubElements == 10, "NumSubElements (%u) == 10", (unsigned int)TypeInfo.NumSubElements); - UtAssert_True(TypeInfo.Size.Bytes == sizeof(UT1_BasicDataTypes_t), "ByteSize (%u) == %u", - (unsigned int)TypeInfo.Size.Bytes, (unsigned int)sizeof(UT1_BasicDataTypes_t)); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_t_DATADICTIONARY); - rc = EdsLib_Lookup_SubMemberByIndex(&GD_BASIC, MsgId, 6, &CompInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(i == 12, "EdsLib_Lookup_SubMemberByIndex(%x) (%u) == 1", - (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(CompInfo.StructureId); - UtAssert_True(i == UTM_EDS_ut_base_types_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_ut_base_types_INDEX", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(CompInfo.StructureId); - UtAssert_True(i == int32_Atom_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == int32_Atom_t_DATADICTIONARY", - (unsigned int)CompInfo.StructureId, (unsigned int)i); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_ut_base_types_INDEX, int32_Atom_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_True(TypeInfo.ElemType == EDSLIB_BASICTYPE_SIGNED_INT, "ElemType (%u) == EDSLIB_BASICTYPE_SIGNED_INT", (unsigned int)TypeInfo.ElemType); - UtAssert_True(TypeInfo.NumSubElements == 0, "NumSubElements (%u) == 0", (unsigned int)TypeInfo.NumSubElements); - UtAssert_True(TypeInfo.Size.Bytes == 4, "ByteSize (%u) == 4", (unsigned int)TypeInfo.Size.Bytes); - - /* Test the dictionary walk function -- not worth doing this unless previous tests are working */ - - WalkTestState.TestName = "InvalidShallow"; - WalkTestState.CurrentLine = Invalid_EXPECTED; - EdsLib_Basic_Iterator_Dictionary(&GD_BASIC, EDSLIB_ID_INVALID, BasicCheckCallback, &WalkTestState); - UtAssert_Simple(WalkTestState.CurrentLine == Invalid_EXPECTED); - - MsgId = EdsLib_Lookup_DerivedType(&GD_BASIC, EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_BasePacket_Intf_t_DATADICTIONARY), - 0, UT1_TestTLM_MSG); - UtAssert_Simple(MsgId != 0); - memset(&WalkTestState, 0, sizeof(WalkTestState)); - WalkTestState.TestName = "PayloadShallow"; - WalkTestState.CurrentLine = PayloadShallow_EXPECTED; - EdsLib_Basic_Iterator_Dictionary(&GD_BASIC, MsgId, BasicCheckCallback, &WalkTestState); - UtAssert_Simple(*WalkTestState.CurrentLine == NULL); - WalkTestState.TestName = "PayloadDeep"; - WalkTestState.DoDescend = true; - WalkTestState.CurrentLine = PayloadDeep_EXPECTED; - EdsLib_Basic_Iterator_Dictionary(&GD_BASIC, MsgId, BasicCheckCallback, &WalkTestState); - UtAssert_Simple(*WalkTestState.CurrentLine == NULL); - WalkTestState.TestName = "PayloadStop"; - WalkTestState.DoStop = true; - WalkTestState.CurrentLine = PayloadStop_EXPECTED; - EdsLib_Basic_Iterator_Dictionary(&GD_BASIC, MsgId, BasicCheckCallback, &WalkTestState); - UtAssert_Simple(*WalkTestState.CurrentLine == NULL); - - - /* Test the unregistration calls */ - - rc = EdsLib_Basic_AppDict_Unregister(&GD_BASIC, UTM_EDS_UT1_INDEX); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc != EDSLIB_SUCCESS); - UtAssert_Simple(TypeInfo.ElemType == EDSLIB_BASICTYPE_NONE); - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_UtPriHdr_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(TypeInfo.ElemType == EDSLIB_BASICTYPE_CONTAINER); - MsgId = EDSLIB_MAKE_ID(UTM_EDS_ut_base_types_INDEX, int32_Atom_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(TypeInfo.ElemType == EDSLIB_BASICTYPE_SIGNED_INT); - - rc = EdsLib_Basic_AppDict_Unregister(&GD_BASIC, UTM_EDS_UTHDR_INDEX); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, UTHDR_UtPriHdr_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc != EDSLIB_SUCCESS); - UtAssert_Simple(TypeInfo.ElemType == EDSLIB_BASICTYPE_NONE); - MsgId = EDSLIB_MAKE_ID(UTM_EDS_ut_base_types_INDEX, int32_Atom_t_DATADICTIONARY); - rc = EdsLib_DataTypeDB_GetTypeInfo(&GD_BASIC, MsgId, &TypeInfo); - UtAssert_Simple(rc == EDSLIB_SUCCESS); - UtAssert_Simple(TypeInfo.ElemType == EDSLIB_BASICTYPE_SIGNED_INT); - -} diff --git a/edslib/unit-test/edslib_full_test.c b/edslib/unit-test/edslib_full_test.c deleted file mode 100644 index 0dcadb6..0000000 --- a/edslib/unit-test/edslib_full_test.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation - * - * Copyright (c) 2020 United States Government as represented by - * the Administrator of the National Aeronautics and Space Administration. - * All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/** - * \file edslib_full_test.c - * \ingroup edslib - * \author joseph.p.hickey@nasa.gov - * - * Unit testing of the "full" (name-based) EDS API - */ - -#include -#include -#include - -#include "utassert.h" - -#include -#include -#include - -#include "edslib_displaydb.h" -#include "edslib_id.h" -#include "UTHDR_msgdefs.h" -#include "UT1_msgdefs.h" - -extern EdsLib_DataDictionary_t *UTM_EDS_DATADICT_APPTBL[UTM_EDS_MAX_INDEX]; -extern EdsLib_NameDictionary_t *UTM_EDS_NAMEDICT_APPTBL[UTM_EDS_MAX_INDEX]; -extern EdsLib_SymbolDictionary_t UTM_EDS_SYMBOL_TABLE; -extern EdsLib_DatabaseObject_t GD_BASIC; - -union -{ - UT1_Cmd_t As_Cmd; - UT1_Tlm1_t As_Tlm1; - uint8_t As_Bytes[1024]; -} Buffer; - -EdsLib_DatabaseObject_t GD_FULL = -{ - .AppTableSize = UTM_EDS_MAX_INDEX, - .AppDataTable = UTM_EDS_DATADICT_APPTBL, - .AppNameTable = UTM_EDS_NAMEDICT_APPTBL, - .SymbolTable = &UTM_EDS_SYMBOL_TABLE -}; - -const char *FullFormats_EXPECTED[] = -{ - "Formats:int8:T=1/O=0/H=0", - "Formats:uint8:T=2/O=0/H=0", - "Formats:int16:T=1/O=0/H=0", - "Formats:uint16:T=2/O=0/H=0", - "Formats:int32:T=1/O=0/H=0", - "Formats:uint32:T=2/O=0/H=0", - "Formats:int64:T=1/O=0/H=0", - "Formats:uint64:T=2/O=0/H=0", - "Formats:memaddr:T=2/O=0/H=0", - "Formats:float:T=3/O=0/H=0", - "Formats:double:T=3/O=0/H=0", - "Formats:UTHDR/BasePacket:T=7/O=0/H=2", - "Formats:UTHDR/UtCmd:T=7/O=0/H=2", - "Formats:UTHDR/UtTlm:T=7/O=0/H=2", - "Formats:UTHDR/UtPriHdr:T=5/O=0/H=2", - "Formats:UTHDR/UtCmdSecHdr:T=5/O=0/H=2", - "Formats:UTHDR/UtTlmSecHdr:T=5/O=0/H=2", - "Formats:UT1/TestCMD:T=7/O=0/H=2", - "Formats:UT1/TestTLM:T=7/O=0/H=2", - "Formats:UT1/Cmd1:T=7/O=0/H=2", - "Formats:UT1/Cmd2:T=7/O=0/H=2", - "Formats:UT1/CMDVAL:T=2/O=0/H=4", - "Formats:UT1/char@10:T=4/O=0/H=3", - "Formats:UT1/uint16@4:T=6/O=0/H=0", - "Formats:UT1/uint8@250:T=6/O=0/H=0", - "Formats:UT1/bitmask@20:T=6/O=0/H=0", - "Formats:UT1/bitmask@40:T=6/O=0/H=0", - "Formats:UT1/bitmask@10:T=6/O=0/H=0", - "Formats:UT1/BasicDataTypes:T=5/O=0/H=2", - "Formats:UT1/BasicDataTypes@5:T=6/O=0/H=0", - "Formats:UT1/Cmd:T=5/O=0/H=2", - "Formats:UT1/Tlm1:T=5/O=0/H=2", - "Formats:UT1/Table1:T=5/O=0/H=2", - NULL -}; - -const char *Symbols_EXPECTED[] = -{ - "Symbols:BASE_NUMBER=14", - "Symbols:BASE_TYPE=0", - "Symbols:NUMBER_OF_WIDGETS=14", - "Symbols:SIGNED_INTEGER_ENCODING=0", - "Symbols:UTHDR_UT_CMD_MID_PER_CPU=30", - "Symbols:UTHDR_UT_PORTABLE_CMD_CPU_MSG_BASE=30", - "Symbols:UTHDR_UT_PORTABLE_TLM_CPU_MSG_BASE=60", - "Symbols:UTHDR_UT_TLM_MID_PER_CPU=30", - NULL -}; - -const char *TlmPayloadShallow_EXPECTED[] = -{ - "TlmPayloadShallow:{NONAME}:T=7/O=0/H=2", - "TlmPayloadShallow:Payload:T=5/O=8/H=2", - NULL -}; - -const char *TlmPayloadDeep_EXPECTED[] = -{ - "TlmPayloadDeep:UtLength:T=2/O=0/H=0", - "TlmPayloadDeep:UtStreamId:T=2/O=2/H=0", - "TlmPayloadDeep:SecHdr.UtCommandId:T=2/O=4/H=0", - "TlmPayloadDeep:SecHdr.CmdExtraInfo:T=2/O=6/H=0", - "TlmPayloadDeep:Payload.Commands[0]:T=2/O=8/H=0", - "TlmPayloadDeep:Payload.Commands[1]:T=2/O=9/H=0", - "TlmPayloadDeep:Payload.Commands[2]:T=2/O=10/H=0", - "TlmPayloadDeep:Payload.Commands[3]:T=2/O=11/H=0", - NULL -}; - -const char *CmdPayloadShallow_EXPECTED[] = -{ - "CmdPayloadShallow:{NONAME}:T=7/O=0/H=2", - "CmdPayloadShallow:Payload:T=5/O=8/H=2", - NULL -}; - -const char *CmdPayloadDeep_EXPECTED[] = -{ - "CmdPayloadDeep:UtLength:T=2/O=0/H=0", - "CmdPayloadDeep:UtStreamId:T=2/O=2/H=0", - "CmdPayloadDeep:SecHdr.UtCommandId:T=2/O=4/H=0", - "CmdPayloadDeep:SecHdr.CmdExtraInfo:T=2/O=6/H=0", - "CmdPayloadDeep:Payload.Commands[0]:T=2/O=8/H=0", - "CmdPayloadDeep:Payload.Commands[1]:T=2/O=9/H=0", - "CmdPayloadDeep:Payload.Commands[2]:T=2/O=10/H=0", - "CmdPayloadDeep:Payload.Commands[3]:T=2/O=11/H=0", - NULL -}; - -const char *BasicDataTypes_EXPECTED[] = -{ - "BasicDataTypes:cmdval:T=2/O=0/H=4", - "BasicDataTypes:b1:T=1/O=1/H=0", - "BasicDataTypes:bl1:T=2/O=2/H=0", - "BasicDataTypes:w1:T=2/O=4/H=0", - "BasicDataTypes:w2:T=1/O=6/H=0", - "BasicDataTypes:dw1:T=2/O=8/H=0", - "BasicDataTypes:dw2:T=1/O=12/H=0", - "BasicDataTypes:f1:T=3/O=16/H=0", - "BasicDataTypes:df1:T=3/O=24/H=0", - "BasicDataTypes:str:T=4/O=32/H=3", -}; - -typedef struct -{ - const char *TestName; - const char **CurrentLine; -} WalkTestState_t; - -static void FullTestCallback(void *Arg, const EdsLib_EntityDescriptor_t *Param) -{ - char OutputBuffer[128]; - const char *TempPtr; - WalkTestState_t *TestState = Arg; - - if (Param->FullName == NULL) - { - TempPtr = "{NONAME}"; - } - else - { - TempPtr = Param->FullName; - } - - snprintf(OutputBuffer, sizeof(OutputBuffer), "%s:%s:T=%u/O=%u/H=%u", - TestState->TestName, - TempPtr, - (unsigned int)Param->Basic.TypeInfo.ElemType, - (unsigned int)Param->Basic.ComponentInfo.AbsOffset, - (unsigned int)Param->Disp.DisplayHint); - - TempPtr = *TestState->CurrentLine; - if (TempPtr == NULL) - { - TempPtr = "{END}"; - } - else - { - ++TestState->CurrentLine; - } - - UtAssert_True(strcmp(TempPtr, OutputBuffer) == 0, "%s", OutputBuffer); -} - -static EdsLib_Iterator_Rc_t FormatCallback(EdsLib_Iterator_CbType_t CbType, void *Arg, EdsLib_Basic_MemberInfo_t *WalkState) -{ - EdsLib_EntityDescriptor_t Desc; - - Desc.Basic = *WalkState; - EdsLib_Get_DisplayHint(&GD_FULL, WalkState->ComponentInfo.StructureId, &Desc.Disp); - Desc.FullName = EdsLib_Get_DescriptiveName(&GD_FULL, WalkState->ComponentInfo.StructureId); - FullTestCallback(Arg, &Desc); - - return EDSLIB_ITERATOR_RC_CONTINUE; -} - -static void TestSymbolCallback(void *Arg, const char *SymbolName, int32_t SymbolValue) -{ - WalkTestState_t *TestState = Arg; - const char *TempPtr; - char OutputBuffer[128]; - - snprintf(OutputBuffer, sizeof(OutputBuffer), "%s:%s=%ld", - TestState->TestName, - SymbolName, - (long)SymbolValue); - - TempPtr = *TestState->CurrentLine; - if (TempPtr == NULL) - { - TempPtr = "{END}"; - } - else - { - ++TestState->CurrentLine; - } - - UtAssert_True(strcmp(TempPtr, OutputBuffer) == 0, "%s", OutputBuffer); -} - -void EdsLib_Full_Test(void) -{ - EdsLib_Id_t MsgId; - char OutputBuffer[128]; - EdsLib_EntityDescriptor_t LocateDesc; - uint16_t i; - const char *StringResult; - int32_t TestResult; - WalkTestState_t WalkTestState; - - WalkTestState.CurrentLine = FullFormats_EXPECTED; - WalkTestState.TestName = "Formats"; - EdsLib_Iterator_Formats(&GD_BASIC, FormatCallback, &WalkTestState); - - WalkTestState.CurrentLine = Symbols_EXPECTED; - WalkTestState.TestName = "Symbols"; - EdsLib_Iterator_Symbol_Namespace(&GD_FULL, NULL, TestSymbolCallback, &WalkTestState); - - for (i=0; i < sizeof(Buffer); ++i) - { - Buffer.As_Bytes[i] = 0xAA ^ (i & 0xFF); - } - //EdsLib_Generate_Hexdump(Logfile, Buffer.As_Bytes, 16, sizeof(UT1_Tlm1_Payload_t)); - //EdsLib_Generate_Hexdump(Logfile, Buffer.As_Bytes, 4, sizeof(UT1_Cmd_Payload_t)); - - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "2:UTHDR/UtTlm"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 2, "EdsLib_Get_CpuNumber(%08x) (%u) == 2", - (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UTHDR_INDEX, "EdsLib_Get_AppIdx(%08x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)i, (unsigned int)UTM_EDS_UTHDR_INDEX); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UTHDR_UtTlm_Intf_t_DATADICTIONARY, "EdsLib_Get_FormatIndex(%08x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)i, (unsigned int)UTHDR_UtTlm_Intf_t_DATADICTIONARY); - - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "4:UT1/Cmd1"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 4, "EdsLib_Get_CpuNumber(%08x) (%u) == 4", - (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%08x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)i, (unsigned int)UTM_EDS_UT1_INDEX); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UT1_Cmd1_Intf_t_DATADICTIONARY, "EdsLib_Get_FormatIndex(%08x) (%u) == %u", - (unsigned int)MsgId, (unsigned int)i, (unsigned int)UT1_Cmd1_Intf_t_DATADICTIONARY); - - WalkTestState.CurrentLine = CmdPayloadShallow_EXPECTED; - WalkTestState.TestName = "CmdPayloadShallow"; - EdsLib_DisplayDB_IterateMembers(&GD_FULL, MsgId, FullTestCallback, &WalkTestState); - WalkTestState.CurrentLine = CmdPayloadDeep_EXPECTED; - WalkTestState.TestName = "CmdPayloadDeep"; - EdsLib_Iterator_Payload_FullNames(&GD_FULL, MsgId, FullTestCallback, &WalkTestState); - - /* FIXME: coverage */ - //EdsLib_MsgId_To_StreamId(&GD_FULL, MsgId); - - if (EdsLib_Endian_NeedsSwap(EDSLIB_BYTE_ORDER_BIG_ENDIAN, EDSLIB_BYTE_ORDER_NATIVE)) - { - EdsLib_SwapInPlace(&GD_FULL, MsgId, Buffer.As_Bytes, 0); - } - - /* For sanity we have to replace the floating points with reasonable values */ - Buffer.As_Tlm1.BasicDataTypes1.f1 = 0.1; - Buffer.As_Tlm1.BasicDataTypes1.df1 = 0.3; - for (i=0; i < 5; ++i) - { - Buffer.As_Tlm1.BasicDataTypes2[i].f1 = i + 1.1; - Buffer.As_Tlm1.BasicDataTypes2[i].df1 = i + 1.3; - } - - StringResult = EdsLib_DisplayDB_GetEdsName(&GD_BASIC, MsgId); - UtAssert_True(strcmp(StringResult,"UNDEFINED") == 0, "EdsLib_DisplayDB_GetEdsName(GD_BASIC, %x) (%s) == UNDEFINED", - (unsigned int)MsgId, StringResult); - StringResult = EdsLib_Get_DescriptiveName(&GD_BASIC, MsgId); - UtAssert_True(strcmp(StringResult,"UNDEFINED") == 0,"EdsLib_Get_DescriptiveName(GD_BASIC, %x) (%s) == UNDEFINED", - (unsigned int)MsgId, StringResult); - StringResult = EdsLib_DisplayDB_GetEdsName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UT1") == 0,"EdsLib_DisplayDB_GetEdsName(GD_FULL, %x) (%s) == UT1", - (unsigned int)MsgId, StringResult); - StringResult = EdsLib_Get_DescriptiveName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UT1/Cmd1") == 0,"EdsLib_Get_DescriptiveName(GD_FULL, %x) (%s) == UT1/Cmd1", - (unsigned int)MsgId, StringResult); - - WalkTestState.CurrentLine = TlmPayloadShallow_EXPECTED; - WalkTestState.TestName = "TlmPayloadShallow"; - EdsLib_DisplayDB_IterateMembers(&GD_FULL, MsgId, FullTestCallback, &WalkTestState); - WalkTestState.CurrentLine = TlmPayloadDeep_EXPECTED; - WalkTestState.TestName = "TlmPayloadDeep"; - EdsLib_Iterator_Payload_FullNames(&GD_FULL, MsgId, FullTestCallback, &WalkTestState); - - WalkTestState.CurrentLine = BasicDataTypes_EXPECTED; - WalkTestState.TestName = "BasicDataTypes"; - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_BasicDataTypes_t_DATADICTIONARY); - EdsLib_Iterator_Payload_FullNames(&GD_FULL, MsgId, FullTestCallback, &WalkTestState); - - memset(&LocateDesc, 0, sizeof(LocateDesc)); - LocateDesc.FullName = "BasicDataTypes2[3].df1"; - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX, UT1_Tlm1_t_DATADICTIONARY); - - TestResult = EdsLib_DisplayDB_LocateSubEntity(&GD_FULL, MsgId, LocateDesc.FullName, &LocateDesc.Basic.ComponentInfo); - UtAssert_True(TestResult == 0, "EdsLib_Locate_Parameter(GD_FULL, %x) (%d) == 0", - (unsigned int)MsgId, (int)TestResult); - UtAssert_True(LocateDesc.Basic.ComponentInfo.AbsOffset == 232, "LocateDesc.Basic.AbsOffset (%u) == 232", - (unsigned int)LocateDesc.Basic.ComponentInfo.AbsOffset); - TestResult = EdsLib_DataTypeDB_GetTypeInfo(&GD_FULL, LocateDesc.Basic.ComponentInfo.StructureId, &LocateDesc.Basic.TypeInfo); - UtAssert_True(TestResult == 0, "EdsLib_DataTypeDB_GetTypeInfo(GD_FULL, %x) (%d) == 0", - (unsigned int)LocateDesc.Basic.ComponentInfo.StructureId, (int)TestResult); - UtAssert_True(LocateDesc.Basic.TypeInfo.Size.Bytes == 8, "LocateDesc.Basic.TypeInfo.ByteSize (%u) == 8", - (unsigned int)LocateDesc.Basic.TypeInfo.Size.Bytes); - UtAssert_True(LocateDesc.Basic.TypeInfo.ElemType == EDSLIB_BASICTYPE_FLOAT, "LocateDesc.Basic.TypeInfo.ElemType (%u) == EDSLIB_ELEMTYPE_IEEE_FLOAT", - (unsigned int)LocateDesc.Basic.TypeInfo.ElemType); - - TestResult = EdsLib_DisplayDB_GetIndexByName(&GD_FULL, MsgId, LocateDesc.FullName, &i); - UtAssert_True(TestResult == 0, "EdsLib_Lookup_SubMemberByName(GD_FULL, %x) (%d) == 0", - (unsigned int)MsgId, (int)TestResult); - UtAssert_True(i == 4, "SubIndex (%u) == 4", (unsigned int)i); - - memset(&LocateDesc, 0, sizeof(LocateDesc)); - - TestResult = EdsLib_DisplayDB_LocateSubEntity(&GD_FULL, MsgId, "BasicDataTypes2{badsyntax}", &LocateDesc.Basic.ComponentInfo); - UtAssert_True(TestResult != 0, "EdsLib_Locate_Parameter(GD_FULL, %x) (%d) != 0", - (unsigned int)MsgId, (int)TestResult); - TestResult = EdsLib_DisplayDB_LocateSubEntity(&GD_FULL, MsgId, "BasicDataTypes2[4].nonexisting", &LocateDesc.Basic.ComponentInfo); - UtAssert_True(TestResult != 0, "EdsLib_Locate_Parameter(GD_FULL, %x) (%d) != 0", - (unsigned int)MsgId, (int)TestResult); - TestResult = EdsLib_DisplayDB_GetIndexByName(&GD_FULL, MsgId, "nonexisting", &i); - UtAssert_True(TestResult != 0, "EdsLib_Lookup_SubMemberByName(GD_FULL, %x) (%d) != 0", - (unsigned int)MsgId, (int)TestResult); - TestResult = EdsLib_DisplayDB_GetIndexByName(&GD_FULL, MsgId, "bitmask16", &i); - UtAssert_True(TestResult == 0, "EdsLib_Lookup_SubMemberByName(GD_FULL, %x) (%d) == 0", - (unsigned int)MsgId, (int)TestResult); - UtAssert_True(i == 2, "SubIndex (%u) == 2", (unsigned int)i); - - EdsLib_Set_FormatIdx(&MsgId, 100); - StringResult = EdsLib_Get_DescriptiveName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UNDEFINED") == 0, "EdsLib_Get_DescriptiveName(GD_FULL, %x) (%s) == UNDEFINED", - (unsigned int)MsgId, StringResult); - MsgId = EDSLIB_MAKE_ID(10,0); - StringResult = EdsLib_DisplayDB_GetEdsName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UNDEFINED") == 0, "EdsLib_DisplayDB_GetEdsName(GD_FULL, %x) (%s) == UNDEFINED", - (unsigned int)MsgId, StringResult); - MsgId = EDSLIB_MAKE_ID(UTM_EDS_UTHDR_INDEX, 100); - StringResult = EdsLib_Get_DescriptiveName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UNDEFINED") == 0, "EdsLib_Get_DescriptiveName(GD_FULL, %x) (%s) == UNDEFINED", - (unsigned int)MsgId, StringResult); - - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "INVALID"); - UtAssert_True(MsgId == EDSLIB_ID_INVALID, "MsgId (%x) == EDSLIB_ID_INVALID", (unsigned int)MsgId); - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "UT1:INVALID"); - UtAssert_True(MsgId == EDSLIB_ID_INVALID, "MsgId (%x) == EDSLIB_ID_INVALID", (unsigned int)MsgId); - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "UT1/BasicDataTypes"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 0, "EdsLib_Get_CpuNumber(%x) (%u) == 0", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UT1_BasicDataTypes_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_BasicDataTypes_t_DATADICTIONARY", (unsigned int)MsgId, (unsigned int)i); - StringResult = EdsLib_Get_DescriptiveName(&GD_FULL, MsgId); - UtAssert_True(strcmp(StringResult,"UT1/BasicDataTypes") == 0, "EdsLib_Get_DescriptiveName(GD_FULL, %x) (%s) == UT1/BasicDataTypes", - (unsigned int)MsgId, StringResult); - - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "INVALID"); - UtAssert_True(MsgId == EDSLIB_ID_INVALID, "MsgId (%x) == EDSLIB_ID_INVALID", (unsigned int)MsgId); - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "1:UT1/Cmd1"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 1, "EdsLib_Get_CpuNumber(%x) (%u) == 1", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UT1_Cmd1_Intf_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_Cmd1_Intf_t_DATADICTIONARY", - (unsigned int)MsgId, (unsigned int)i); - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "2:UT1/Tlm1"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 2, "EdsLib_Get_CpuNumber(%x) (%u) == 2", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UT1_Tlm1_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_Tlm1_t_DATADICTIONARY", (unsigned int)MsgId, (unsigned int)i); - MsgId = EdsLib_DisplayDB_LookupTypeName(&GD_FULL, "3:UT1/TestCMD"); - i = EdsLib_Get_CpuNumber(MsgId); - UtAssert_True(i == 3, "EdsLib_Get_CpuNumber(%x) (%u) == 3", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_AppIdx(MsgId); - UtAssert_True(i == UTM_EDS_UT1_INDEX, "EdsLib_Get_AppIdx(%x) (%u) == UTM_EDS_UT1_INDEX", (unsigned int)MsgId, (unsigned int)i); - i = EdsLib_Get_FormatIdx(MsgId); - UtAssert_True(i == UT1_TestCMD_Intf_t_DATADICTIONARY, "EdsLib_Get_FormatIdx(%x) (%u) == UT1_TestCMD_Intf_t_DATADICTIONARY", - (unsigned int)MsgId, (unsigned int)i); - EdsLib_GlobalMessageId_ToString(&GD_FULL, OutputBuffer, sizeof(OutputBuffer), MsgId); - UtAssert_True(strcmp(OutputBuffer, "3:UT1/TestCMD") == 0, "EdsLib_GlobalMessageId_ToString(..., %x) (%s) == 3:UT1/TestCMD", - (unsigned int)MsgId, OutputBuffer); - //EdsLib_MsgId_To_StreamId(&GD_FULL, MsgId); /* FIXME: coverage */ -} - -void EdsLib_StringConv_Test(void) -{ - EdsLib_NumberBuffer_t ValBuf; - EdsLib_DisplayHint_t Conv; - int32_t TestResult; - char TestBuffer[128]; - - /* Perform some to/from string conversions - must hit every type */ - /* A conv that has been "zeroed out" should be safe */ - memset(&Conv, 0, sizeof(Conv)); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 1, "INVALID", EDSLIB_BASICTYPE_NONE, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(..., INVALID) (%d) != 0", (int)TestResult); - - /* Test conversion of strings to signed ints of various sizes */ - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 1, "-10", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(i8) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.i8 == -10, "ValBuf.i8 (%d) == -10", (int)ValBuf.i8); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 2, "-15743", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(i16) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.i16 == -15743, "ValBuf.i16 (%d) == -15743", (int)ValBuf.i16); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "-1045203485", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(i32) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.i32 == -1045203485, "ValBuf.i32 (%d) == -1045203485", (int)ValBuf.i32); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 8, "-5968483949505", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(i64) (%d) == 0", (int)TestResult); - /* NOTE: On 32 bit machines the assertion here should still work but the message may be wrong */ - UtAssert_True(ValBuf.i64 == -5968483949505, "ValBuf.i64 (%ld) == -5968483949505", (long)ValBuf.i64); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 0, "xxx", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(non-integer) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 10, "123", EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(bad size) (%d) != 0", (int)TestResult); - - /* Test conversion of signed ints of various sizes to strings */ - ValBuf.i8 = -11; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 1, EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(i8) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "-11") == 0, "OutputBuffer(%s) == -11", TestBuffer); - ValBuf.i16 = -15744; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 2, EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(i16) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "-15744") == 0, "OutputBuffer(%s) == -15744", TestBuffer); - ValBuf.i32 = -1045203486; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(i32) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "-1045203486") == 0, "OutputBuffer(%s) == -1045203486", TestBuffer); - ValBuf.i64 = -5968483949506; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 8, EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(i64) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "-5968483949506") == 0, "OutputBuffer(%s) == -5968483949506", TestBuffer); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 13, EDSLIB_BASICTYPE_SIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_ToString(bad_size) (%d) != 0", (int)TestResult); - - /* Test conversion of strings to unsigned ints of various sizes */ - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 1, "250", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(u8) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.u8 == 250, "ValBuf.u8 (%u) == 250", (unsigned int)ValBuf.u8 ); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 2, "40321", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(u16) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.u16 == 40321, "ValBuf.u16 (%u) == 40321", (unsigned int)ValBuf.u16); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "3194304358", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(u32) (%d) == 0)",(int)TestResult); - UtAssert_True(ValBuf.u32 == 3194304358, "ValBuf.u32 (%lu) == 3194304358", (unsigned long)ValBuf.u32); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 8, "59486574499385", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(u64) (%d) == 0",(int)TestResult); - /* NOTE: On 32 bit machines the assertion here should still work but the message may be wrong */ - UtAssert_True(ValBuf.u64 == 59486574499385, "ValBuf.u64 (%lu) == 59486574499385", (unsigned long)ValBuf.u64); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 0, "xxx", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(non-integer) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 3, "123", EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(bad size) (%d) != 0", (int)TestResult); - - /* Test conversion of unsigned ints of various sizes to strings */ - ValBuf.u8 = 215; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 1, EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(u8) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "215") == 0, "OutputBuffer(%s) == 215", TestBuffer); - ValBuf.u16 = 35645; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 2, EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(u16) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "35645") == 0, "OutputBuffer(%s) == 35645", TestBuffer); - ValBuf.u32 = 3194304394; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(u32) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "3194304394") == 0, "OutputBuffer(%s) == 3194304394", TestBuffer); - ValBuf.u64 = 59486574499193; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 8, EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(u64) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "59486574499193") == 0, "OutputBuffer(%s) == 59486574499193", TestBuffer); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 3, EDSLIB_BASICTYPE_UNSIGNED_INT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_ToString(bad_size) (%d) != 0", (int)TestResult); - - /* - * Test conversion of strings to floats and doubles - * NOTE: specific values were chosen such that they are exactly representable in IEEE754 number space. - * (it must pass an exact equality check) - */ - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "2.5", EDSLIB_BASICTYPE_FLOAT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(f32) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.f32 == 2.5, "ValBuf.flt (%3.1f) == 2.5", (float)ValBuf.f32); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 8, "-3.5", EDSLIB_BASICTYPE_FLOAT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(f64) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.f64 == -3.5, "ValBuf.dbl (%3.1lf) == -3.5", (double)ValBuf.f64); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 0, "xxx", EDSLIB_BASICTYPE_FLOAT, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(non-number) (%d) != 0", (int)TestResult); - - /* Test conversion of floats and doubles to strings */ - ValBuf.f32 = -6.25; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_FLOAT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(f32) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "-6.25") == 0, "OutputBuffer(%s) == -6.25", TestBuffer); - ValBuf.f64 = 17.75; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 8, EDSLIB_BASICTYPE_FLOAT, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(f64) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "17.75") == 0, "OutputBuffer(%s) == 17.75", TestBuffer); - - /* Test conversion of other element types (non-numeric entities) */ - /* A "raw binary" from string should be rejected for now (base64 encoding is a possible way to accept this) */ - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 6, "blahblahblah", EDSLIB_BASICTYPE_BINARY, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(binary) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_BINARY, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(binary) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "") == 0, "OutputBuffer(%s) == ", TestBuffer); - - /* Containers/Interfaces/Arrays cannot be produced directly from a string right now */ - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 6, "blahblahblah", EDSLIB_BASICTYPE_CONTAINER, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(container) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 6, EDSLIB_BASICTYPE_CONTAINER, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(container) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "") == 0, "OutputBuffer(%s) == ", TestBuffer); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 6, "blahblahblah", EDSLIB_BASICTYPE_ARRAY, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(array) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 6, EDSLIB_BASICTYPE_ARRAY, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(array) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "") == 0, "OutputBuffer(%s) == ", TestBuffer); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 6, "blahblahblah", EDSLIB_BASICTYPE_INTERFACE, NULL); - UtAssert_True(TestResult != 0, "EdsLib_Scalar_FromString(interface) (%d) != 0", (int)TestResult); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 6, EDSLIB_BASICTYPE_INTERFACE, NULL); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(interface) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "") == 0, "OutputBuffer(%s) == ", TestBuffer); - - /* Test conversions using "display hints" */ - - /* - * The "fixed name" display hint will force a specific name for the item - * It is only relevant for output (EdsLib_Scalar_ToString). - */ - Conv.DisplayHint = EDSLIB_DISPLAYHINT_FIXEDNAME; - Conv.HintArg = "MyFixedName"; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_BINARY, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(fixedname) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "MyFixedName") == 0, "OutputBuffer(%s) == MyFixedName", TestBuffer); - - /* - * The "char string" display hint will interpret the binary data as a character string - * note that null termination is _not_ required for strings within containers, but should - * be truncated to the right size - */ - Conv.DisplayHint = EDSLIB_DISPLAYHINT_CHAR_STRING; - Conv.HintArg = NULL; - memset(ValBuf.chrs, 'y', 7); - ValBuf.chrs[7] = 0; - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "12345678", EDSLIB_BASICTYPE_BINARY, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(string) (%d) == 0", (int)TestResult); - UtAssert_True(memcmp(ValBuf.chrs, "1234yyy\0", 8) == 0, "chrs(%s) == 1234yyy\\0", ValBuf.chrs); - memset(ValBuf.chrs, 'v', 7); - ValBuf.chrs[7] = 0; - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 6, "1234", EDSLIB_BASICTYPE_BINARY, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(string) (%d) == 0", (int)TestResult); - UtAssert_True(memcmp(ValBuf.chrs, "1234\0\0v\0", 8) == 0, "chrs(%s) == 1234\\0\\0v\\0", ValBuf.chrs); - memset(ValBuf.chrs, 'z', 7); - ValBuf.chrs[7] = 0; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 5, EDSLIB_BASICTYPE_BINARY, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(string) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "zzzzz") == 0, "TestBuffer(%s) == zzzzz", TestBuffer); - memcpy(ValBuf.chrs, "ab\0cd\033ef", 8); - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 8, EDSLIB_BASICTYPE_BINARY, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(string) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "abcd.ef") == 0, "TestBuffer(%s) == abcd.ef", TestBuffer); - - /* - * Memory Addresses are unsigned ints, but should always be hex - * Note: Actual use of this feature is discouraged but supported for now - */ - Conv.DisplayHint = EDSLIB_DISPLAYHINT_ADDRESS; - Conv.HintArg = NULL; - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "1234", EDSLIB_BASICTYPE_UNSIGNED_INT, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(address) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.u32 == 0x1234, "ValBuf.u32 (0x%lx) == 0x1234", (unsigned long)ValBuf.u32); - ValBuf.u32 = 1234; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 4, EDSLIB_BASICTYPE_UNSIGNED_INT, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(address) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "0x000004d2") == 0, "OutputBuffer(%s) == 0x000004d2", TestBuffer); - - /* - * Enumerations use a lookup table to determine a user-friendly name for the value - */ - EdsLib_Get_DisplayHint(&GD_FULL, EDSLIB_MAKE_ID(UTM_EDS_UT1_INDEX,UT1_CMDVAL_Enum_t_DATADICTIONARY), &Conv); - UtAssert_Simple(Conv.DisplayHint == EDSLIB_DISPLAYHINT_ENUM_SYMTABLE); - UtAssert_Simple(Conv.HintArg != NULL); - TestResult = EdsLib_Scalar_FromString(ValBuf.bytes, 4, "C144", EDSLIB_BASICTYPE_UNSIGNED_INT, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_FromString(enum) (%d) == 0", (int)TestResult); - UtAssert_True(ValBuf.u32 == 144, "ValBuf.u32 (%lu) == 144", (unsigned long)ValBuf.u32); - ValBuf.i16 = 160; - TestResult = EdsLib_Scalar_ToString(TestBuffer, sizeof(TestBuffer), ValBuf.bytes, 2, EDSLIB_BASICTYPE_UNSIGNED_INT, &Conv); - UtAssert_True(TestResult == 0, "EdsLib_Scalar_ToString(enum) (%d) == 0", (int)TestResult); - UtAssert_True(strcmp(TestBuffer, "C160") == 0, "OutputBuffer(%s) == C160", TestBuffer); - -} - diff --git a/edslib/unit-test/edslib_test.c b/edslib/unit-test/edslib_test.c deleted file mode 100644 index 82269dd..0000000 --- a/edslib/unit-test/edslib_test.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation - * - * Copyright (c) 2020 United States Government as represented by - * the Administrator of the National Aeronautics and Space Administration. - * All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/** - * \file edslib_test.c - * \ingroup edslib - * \author joseph.p.hickey@nasa.gov - * - * Unit test entry point - */ - -#include "utassert.h" -#include "uttest.h" - -extern void EdsLib_Basic_Test(void); -extern void EdsLib_Full_Test(void); -extern void EdsLib_StringConv_Test(void); - -void UtTest_Setup(void) -{ - UtTest_Add(EdsLib_Basic_Test, NULL, NULL, "EDS Basic"); - UtTest_Add(EdsLib_Full_Test, NULL, NULL, "EDS Full"); - UtTest_Add(EdsLib_StringConv_Test, NULL, NULL, "EDS String Conversions"); -} - diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt index e70d911..c7d246a 100644 --- a/tool/CMakeLists.txt +++ b/tool/CMakeLists.txt @@ -30,7 +30,7 @@ # tools are linked into the final executables, but there is no dependency # on expat in the final binaries. # -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(EDSTOOL C) add_definitions(-Wall -Werror -std=c99 -pedantic) @@ -64,6 +64,7 @@ add_executable(sedstool src/seds_tree_node.c src/seds_instance_node.c src/seds_memreq.c + src/seds_range.c src/seds_outputfile.c src/seds_plugin.c src/seds_user_message.c diff --git a/tool/cmake/edstool-execute-all.mk b/tool/cmake/edstool-execute-all.mk new file mode 100644 index 0000000..94e1507 --- /dev/null +++ b/tool/cmake/edstool-execute-all.mk @@ -0,0 +1,59 @@ +##################################################### +# +# CFE/CFS mission build makefile helper +# This calls the EDS tooling to generate all artifacts +# The process also includes building those artifacts for the host +# +##################################################### + +# This checks the sanity of the calling environment. If any of these fail +# it indicates a problem in the CMake target that invokes this makefile. +ifeq ($(EDSTOOL_PROJECT_NAME),) +$(error EDSTOOL_PROJECT_NAME not defined) +endif +ifeq ($(O),) +$(error O is not defined) +endif +ifeq ($(S),) +$(error S is not defined) +endif + +EDSNAMESPACE := edstool-namespace-${EDSTOOL_PROJECT_NAME}.mk +EDSSOURCES := edstool-sources-${EDSTOOL_PROJECT_NAME}.mk + +LOCAL_STAMPFILE := $(O)/edstool-execute-${EDSTOOL_PROJECT_NAME}.stamp + +# Read all context info from files exported from CMake +include $(EDSNAMESPACE) +include $(EDSSOURCES) +include $(O)/edstool-buildenv.mk + +# Sanity check: all of these vars should have been part of the context info +ifeq ($(EDSTOOL_OUTPUT_DIR),) +$(error EDSTOOL_OUTPUT_DIR not defined) +endif +ifeq ($(EDS_FILE_PREFIX),) +$(error EDS_FILE_PREFIX not defined) +endif +ifeq ($(EDS_SYMBOL_PREFIX),) +$(error EDS_SYMBOL_PREFIX not defined) +endif + +# NOTE: At the mission scope, this runs the full EDS tool to generate all artifacts +# As part of this, it invokes the db_objects make scripts as well, so there is noting +# extra that needs to be invoked from here. + +.PHONY: all +all: $(LOCAL_STAMPFILE) + @echo All host side global EDS objects built + +# The file lists here came from CMake, and so they have the semicolon separator, +# which needs to be changed to a space separator for interpretation here. +ALL_EDS_FILES += $(subst ;, ,$(EDSTOOL_FILELIST)) +# The scripts are prefixed with a number that indiciate its execution order +# sorting it here just before using it makes it easier to assemble the lists piecemeal +ALL_EDS_FILES += $(sort $(subst ;, ,$(EDSTOOL_SCRIPTLIST))) + +$(LOCAL_STAMPFILE): $(EDSTOOL) $(EDSNAMESPACE) $(EDSBUILDENV) $(EDSSOURCES) $(ALL_EDS_FILES) + +$(EDSTOOL) -DMAKE_PROGRAM="$(MAKE)" -DOBJDIR="$(O)" -DSRCDIR="$(S)" $(ALL_EDS_FILES) + $(CMAKE) -E touch "$(@)" diff --git a/tool/cmake/edstool-execute-target.cmake b/tool/cmake/edstool-execute-target.cmake new file mode 100644 index 0000000..d224c7d --- /dev/null +++ b/tool/cmake/edstool-execute-target.cmake @@ -0,0 +1,115 @@ +# +# LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation +# +# Copyright (c) 2020 United States Government as represented by +# the Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Get a reference to the top of the git repo for the whole EdsLib submodule. +# All sub-directory refs under this path should be at a consistent relative path +# because they are all controlled together. +get_filename_component(EDS_REPO_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + +function (edstool_find_eds_sources EDSTOOL_PROJECT_NAME EDSTOOL_OUTPUT_DIR) + + # Sanity check: The directory should have been created already + if (NOT IS_DIRECTORY "${EDSTOOL_OUTPUT_DIR}") + message(FATAL_ERROR "Output directory does not exist: ${EDSTOOL_OUTPUT_DIR}") + endif() + + set(CURRENT_EDSTOOL_SCRIPTLIST) + set(CURRENT_EDSTOOL_FILELIST) + + + file(GLOB_RECURSE CURRENT_EDSTOOL_SCRIPTLIST FOLLOW_SYMLINKS + ${EDS_REPO_SOURCE_DIR}/tool/scripts/*.lua + ) + + foreach(DIR ${ARGN}) + if (IS_DIRECTORY ${DIR}/eds) + file(GLOB_RECURSE DIRXML FOLLOW_SYMLINKS ${DIR}/eds/*.xml) + file(GLOB_RECURSE DIRSCRIPTS FOLLOW_SYMLINKS ${DIR}/eds/*.lua) + list(APPEND CURRENT_EDSTOOL_FILELIST ${DIRXML}) + list(APPEND CURRENT_EDSTOOL_SCRIPTLIST ${DIRSCRIPTS}) + unset(DIRXML) + unset(DIRSCRIPTS) + endif() + endforeach() + + # EDS toolchain plugin scripts may have ordering dependencies between them, + # so the assembled set of available scripts should be sorted by its filename + # This allows a numeric prefix (NN-) to be prepended in cases where + # the execution order is a concern (similar to UNIX SysV init system approach) + set(EDS_SCRIPT_BASENAMES) + foreach(SCRIPTFILE ${CURRENT_EDSTOOL_SCRIPTLIST}) + get_filename_component(BASENAME ${SCRIPTFILE} NAME_WE) + set(${BASENAME}_SCRIPT_LOCATION ${SCRIPTFILE}) + list(APPEND EDS_SCRIPT_BASENAMES ${BASENAME}) + endforeach(SCRIPTFILE ${CURRENT_EDSTOOL_SCRIPTLIST}) + + # Now rebuild the script list sorted by basename + list(SORT EDS_SCRIPT_BASENAMES) + set(CURRENT_EDSTOOL_SCRIPTLIST) + foreach(BASENAME ${EDS_SCRIPT_BASENAMES}) + list(APPEND CURRENT_EDSTOOL_SCRIPTLIST ${${BASENAME}_SCRIPT_LOCATION}) + unset(${BASENAME}_SCRIPT_LOCATION) + endforeach(BASENAME ${EDS_SCRIPT_BASENAMES}) + unset(EDS_SCRIPT_BASENAMES) + + # Export the set of EDS source files + configure_file( + ${EDS_REPO_SOURCE_DIR}/tool/cmake/edstool-sources.mk.in + ${EDSTOOL_OUTPUT_DIR}/edstool-sources-${EDSTOOL_PROJECT_NAME}.mk + ) + +endfunction() + + +function(edstool_setup_toplevel TARGET_NAME EDSTOOL_PROJECT_NAME EDSTOOL_OUTPUT_DIR) + + # Sanity check: The directory should have been created already + if (NOT IS_DIRECTORY "${EDSTOOL_OUTPUT_DIR}") + message(FATAL_ERROR "Output directory does not exist: ${EDSTOOL_OUTPUT_DIR}") + endif() + + # Capture critical variables from the build environment so they can be + # used in custom makefiles that are invoked from here as custom targets + string(TOLOWER ${EDSTOOL_PROJECT_NAME}_eds EDS_FILE_PREFIX) + string(TOUPPER ${EDSTOOL_PROJECT_NAME} EDS_SYMBOL_PREFIX) + + configure_file( + ${EDS_REPO_SOURCE_DIR}/tool/cmake/edstool-namespace.mk.in + ${EDSTOOL_OUTPUT_DIR}/edstool-namespace-${EDSTOOL_PROJECT_NAME}.mk + ) + + # This target builds the full set of artifacts + add_custom_target(${TARGET_NAME} + COMMAND "\$(MAKE)" + EDSTOOL=$ + EDSTOOL_PROJECT_NAME="${EDSTOOL_PROJECT_NAME}" + O=obj + S=src + -f ${EDS_REPO_SOURCE_DIR}/tool/cmake/edstool-execute-all.mk + all + WORKING_DIRECTORY + ${EDSTOOL_OUTPUT_DIR} + DEPENDS + ${EDSTOOL_OUTPUT_DIR}/edstool-namespace-${EDSTOOL_PROJECT_NAME}.mk + ${EDSTOOL_OUTPUT_DIR}/edstool-sources-${EDSTOOL_PROJECT_NAME}.mk + sedstool + ) + +endfunction() diff --git a/tool/cmake/edstool-namespace.mk.in b/tool/cmake/edstool-namespace.mk.in new file mode 100644 index 0000000..211a858 --- /dev/null +++ b/tool/cmake/edstool-namespace.mk.in @@ -0,0 +1,12 @@ +# Variables related to the file and symbol naming conventions +export EDSTOOL_PROJECT_NAME := ${EDSTOOL_PROJECT_NAME} +export EDSTOOL_OUTPUT_DIR := ${EDSTOOL_OUTPUT_DIR} +export EDS_FILE_PREFIX := ${EDS_FILE_PREFIX} +export EDS_SYMBOL_PREFIX := ${EDS_SYMBOL_PREFIX} + +# These are included for reference, in case CMAKE is also used by sub-scripts +export CMAKE := ${CMAKE_COMMAND} +export BUILD_TYPE := ${CMAKE_BUILD_TYPE} + +# Also include the full path to the parent repo +export EDS_REPO_SOURCE_DIR := ${EDS_REPO_SOURCE_DIR} diff --git a/cfecfs/missionlib/cmake/edstool-sources.d.in b/tool/cmake/edstool-sources.mk.in similarity index 58% rename from cfecfs/missionlib/cmake/edstool-sources.d.in rename to tool/cmake/edstool-sources.mk.in index 2579b31..305d15f 100644 --- a/cfecfs/missionlib/cmake/edstool-sources.d.in +++ b/tool/cmake/edstool-sources.mk.in @@ -1,5 +1,5 @@ # Capture the complete list of EDS files and scripts in use # Note that these will be cmake style semicolon-separated, not space-separated. -MISSION_EDS_FILELIST := ${MISSION_EDS_FILELIST} -MISSION_EDS_SCRIPTLIST := ${MISSION_EDS_SCRIPTLIST} +EDSTOOL_FILELIST := ${CURRENT_EDSTOOL_FILELIST} +EDSTOOL_SCRIPTLIST := ${CURRENT_EDSTOOL_SCRIPTLIST} diff --git a/tool/scripts/10-seds_resolve_refs.lua b/tool/scripts/10-seds_resolve_refs.lua index 680158e..9867d97 100644 --- a/tool/scripts/10-seds_resolve_refs.lua +++ b/tool/scripts/10-seds_resolve_refs.lua @@ -89,98 +89,83 @@ end -- Helper function to determine the minimum and maximum value of an Enum local function resolve_enum_range(node,attr) - local rmin - local rmax + + local erange = SEDS.new_rangesegment() for ent in node:iterate_subtree("ENUMERATION_ENTRY") do if (not ent.value) then ent:error("Enum missing value") else - if (not rmin or not rmin.value) then - rmin = { value = ent.value, inclusive = true } - elseif(ent.value < rmin.value) then - rmin.value = ent.value + if (not erange.min_value) then + erange.min_value = ent.value + erange.min_inclusive = true + elseif(ent.value < erange.min_value) then + erange.min_value = ent.value end - if (not rmax or not rmax.value) then - rmax = { value = ent.value, inclusive = true } - elseif(ent.value > rmax.value) then - rmax.value = ent.value + + if (not erange.max_value) then + erange.max_value = ent.value + erange.max_inclusive = true + elseif(ent.value > erange.max_value) then + erange.max_value = ent.value end end end - return { min = rmin, max = rmax } + if (erange.min_value == nil or erange.max_value == nil) then + ent:error("Enum data type has undefined range") + end + + return erange end -- Helper function to determine the possible range of an -- element where minimum and maximum are specified local function resolve_minmax_range(rangedef) - local rmin = rangedef.attributes.min - local rmax = rangedef.attributes.max - local subrange = { } - - if (rangedef.attributes.rangetype) then - local resolved_text = string.lower(rangedef.attributes.rangetype) - if (resolved_text == "exclusiveminexclusivemax") then - subrange.min = { value = rmin, inclusive = false } - subrange.max = { value = rmax, inclusive = false } - elseif(resolved_text == "inclusivemininclusivemax") then - subrange.min = { value = rmin, inclusive = true } - subrange.max = { value = rmax, inclusive = true } - elseif(resolved_text == "inclusiveminexclusivemax") then - subrange.min = { value = rmin, inclusive = true } - subrange.max = { value = rmax, inclusive = false } - elseif (resolved_text == "exclusivemininclusivemax") then - subrange.min = { value = rmin, inclusive = false } - subrange.max = { value = rmax, inclusive = true } - elseif (resolved_text == "greaterthan") then - subrange.min = { value = rmin, inclusive = false } - elseif (resolved_text == "atleast") then - subrange.min = { value = rmin, inclusive = true } - elseif (resolved_text == "lessthan") then - subrange.max = { value = rmax, inclusive = false } - elseif (resolved_text == "atmost") then - subrange.max = { value = rmax, inclusive = true } - end + local subrange = SEDS.new_rangesegment( + rangedef.attributes.min, + rangedef.attributes.max, + rangedef.attributes.rangetype + ) + + return subrange +end + +local function resolve_enumerated_range(rangedef) + + local subrange = SEDS.new_range_empty() + + for n in rangedef:iterate_subtree("LABEL") do + subrange = subrange:union(n.xml_cdata) end return subrange end -- Helper function to calculate the absolute/final range --- of a range EDS element. This is basically the union +-- of a range EDS element. This is basically the intersection -- of all limiting elements beneath it. local function resolve_range(rangenode) - local subrange = {} + local subrange = SEDS.new_range_infinite() -- merge all the range definitions into a single one for n in rangenode:iterate_subtree() do if (n.resolved_range) then - if (not subrange.min or not subrange.min.value) then - subrange.min = n.resolved_range.min - elseif(n.resolved_range.min and n.resolved_range.min.value) then - if (n.resolved_range.min.value > subrange.min.value) then - subrange.min = n.resolved_range.min - elseif (n.resolved_range.min.value == subrange.min.value) then - subrange.min.inclusive = n.resolved_range.min.inclusive and subrange.min.inclusive - end - end - if (not subrange.max or not subrange.max.value) then - subrange.max = n.resolved_range.max - elseif(n.resolved_range.max and n.resolved_range.max.value) then - if (n.resolved_range.max.value < subrange.max.value) then - subrange.max = n.resolved_range.max - elseif (n.resolved_range.max.value == subrange.max.value) then - subrange.max.inclusive = n.resolved_range.max.inclusive and subrange.max.inclusive - end - end + subrange = subrange:intersection(n.resolved_range) end end return subrange end +-- Helper function to initialize a singleton range from a "value" attribute +local function resolve_range_from_valueattr(node) + + local subrange = node.attributes.value + return subrange +end + -- Helper function to create a "name" attribute for datasheet objects -- This is an extension; the XML schema does not indicate any "name" attribute -- on Datasheet elements, but it can be useful for future file generation. @@ -192,6 +177,23 @@ local function resolve_datasheet_basename(node) return basename end +-- Helper function to determine the generic type reference +local function resolve_generictype_ref(node) + local result + local parent_intf = node:find_parent(SEDS.referredintf_filter) + + if (parent_intf.type) then + for gt in parent_intf.type:iterate_subtree(SEDS.generictype_filter) do + if (gt.name == node.name) then + result = gt + break + end + end + end + + return result +end + -- ------------------------------------------------------------------------- -- MASTER TABLE OF ALL SEDS ATTRIBUTES NEEDING RESOLUTION HERE -- This table is used locally by "resolve_attribs" but not exposed to the outside world @@ -316,10 +318,25 @@ local SEDS_ATTRIBUTE_TABLE = { resolved_range = { impl=resolve_minmax_range }, }, + ENUMERATED_RANGE = + { + resolved_range = { impl=resolve_enumerated_range }, + }, RANGE_CONSTRAINT = { - min = { style="integer" }, - max = { style="integer" } + resolved_range = { impl=resolve_range }, + }, + PRESENCE_RANGE_CONSTRAINT = + { + resolved_range = { impl=resolve_range }, + }, + VALUE_CONSTRAINT = + { + resolved_range = { impl=resolve_range_from_valueattr }, + }, + PRESENCE_VALUE_CONSTRAINT = + { + resolved_range = { impl=resolve_range_from_valueattr }, }, BINARY_DATATYPE = { @@ -339,7 +356,8 @@ local SEDS_ATTRIBUTE_TABLE = }, GENERIC_TYPE_MAP = { - type = { style="link", filter=SEDS.any_datatype_filter, required = true } + type = { style="link", filter=SEDS.concrete_datatype_filter, required = true }, + ref = { impl=resolve_generictype_ref, required = true } }, ARGUMENT = { @@ -373,7 +391,7 @@ local SEDS_ATTRIBUTE_TABLE = }, INSTANCE_RULE = { - component = { style="link", filter=SEDS.component_filter } + component = { style="link", filter=SEDS.component_filter, required = false } }, INTERFACE_MAP = { @@ -436,7 +454,7 @@ local function resolve_attribs(node) result = string.lower(tostring(result or resolve.default)) end - if(result == nil and (resolve.required or node.xml_attrs[attr])) then + if(result == nil and (resolve.required == nil and node.xml_attrs[attr] ~= nil)) then node:error(string.format("attribute \'%s\' could not be resolved",attr),node.attributes[attr]) end @@ -466,7 +484,7 @@ for node in SEDS.root:iterate_subtree() do link:mark_reference(node,resolve.mark_reference) end node[attr] = link - elseif(resolve.required or node.xml_attrs[attr]) then + elseif(resolve.required or (resolve.required == nil and node.xml_attrs[attr] ~= nil)) then node:error(string.format("attribute \'%s\' could not be resolved",attr),node.attributes[attr]) end end @@ -479,4 +497,4 @@ end -- This will resolve the remainder of attributes that was not done during pass 1. resolve_attribs(SEDS.root) -SEDS.info ("SEDS resolve refs START") +SEDS.info ("SEDS resolve refs END") diff --git a/tool/scripts/15-seds_create_implicit_scalars.lua b/tool/scripts/15-seds_create_implicit_scalars.lua index 9dc8a7b..8623594 100644 --- a/tool/scripts/15-seds_create_implicit_scalars.lua +++ b/tool/scripts/15-seds_create_implicit_scalars.lua @@ -169,6 +169,7 @@ for _,datatypeset in ipairs(datatypesets) do dimension_list.parent = implicit_arraynode implicit_arraynode.datatyperef = typeref + implicit_arraynode.implicit_basetype = typeref typeref = implicit_arraynode end diff --git a/tool/scripts/20-seds_resolve_sizes.lua b/tool/scripts/20-seds_resolve_sizes.lua index 8b6f95c..d557a54 100644 --- a/tool/scripts/20-seds_resolve_sizes.lua +++ b/tool/scripts/20-seds_resolve_sizes.lua @@ -55,16 +55,16 @@ local function get_integer_bits_from_range(range, enctype) local maxdigits local bitsperdigit - if (range.max and range.max.value) then - max = range.max.value - if (not range.max.inclusive) then + if (range.max_value) then + max = range.max_value + if (not range.max_inclusive) then max = max - 1 end end - if (range.min and range.min.value) then - min = range.min.value - if (not range.min.inclusive) then + if (range.min_value) then + min = range.min_value + if (not range.min_inclusive) then min = min + 1 end else @@ -79,7 +79,7 @@ local function get_integer_bits_from_range(range, enctype) if (enctype == "packedbcd" or enctype == "bcd") then -- determine number of digits in positive range if (max > 0) then - local e = math.log10(max) + local e = math.log(max, 10) -- currently no sign indicator for positive values positivedigits = math.ceil(math.abs(e)) else @@ -88,7 +88,7 @@ local function get_integer_bits_from_range(range, enctype) -- determine number of digits in negative range if (min < 0) then - local e = math.log10(-min) + local e = math.log(-min, 10) -- add 1 for the "minus" indicator negativedigits = 1 + math.ceil(math.abs(e)) else @@ -104,42 +104,7 @@ local function get_integer_bits_from_range(range, enctype) else bitsperdigit = 1 - -- determine number of digits in positive range - if (max > 0) then - local v,e = math.frexp(max) - positivedigits = (e > 1) and e or 1 - -- Because numbers are internally "double" values, - -- anything above ~52 bits may result in an extra - -- digit due to rounding up. This adds a "slop factor" - -- to avoid triggering false errors (the cost is that - -- this might not flag a borderline case that is an error) - if (positivedigits > 52 and v < 0.55) then - positivedigits = positivedigits - 1 - end - else - positivedigits = 0 - end - - -- determine number of digits in negative range - if (min < 0) then - local v,e = math.frexp(-min) - negativedigits = (e > 1) and (e+1) or 2 - -- same as positive case - if (negativedigits > 52 and math.abs(v) < 0.55) then - negativedigits = negativedigits - 1 - elseif (v == 0.5 and (not enctype or enctype == "twoscomplement")) then - -- special handling for twos complement, has one extra - -- value of range on the negative side - negativedigits = negativedigits - 1 - end - - -- In base 2 a sign bit is always needed - -- account for extra bit on the positive side, too - positivedigits = 1 + positivedigits - else - negativedigits = 0 - end - + positivedigits,negativedigits = SEDS.calulate_range_digits(max,min,enctype) end -- the size of the field will be the larger of either @@ -224,7 +189,7 @@ local function get_integer_size(encnode,rangenode) end -- the sign consumes space for one digit, which must be subtracted - limit = math.pow(base,digits - (signed and 1 or 0)) + limit = base ^ (digits - (signed and 1 or 0)) if (limit > base) then if (signed) then @@ -319,14 +284,26 @@ local function resolve_container_datatype_size(node) -- 1. whether or not we have any non-simple entries (error control, length, list, etc) -- 2. what the possible range of each entry is local special_fields = false + local variable_presence = false for idx,off in ipairs(offsets) do local refnode = off.entry local vrange local resolved_range + local presence_node if (refnode) then special_fields = special_fields or refnode.entity_type ~= "CONTAINER_ENTRY" vrange = refnode:find_first("VALID_RANGE") resolved_range = vrange and vrange.resolved_range + presence_node = refnode:find_first("PRESENT_WHEN") + if (presence_node) then + variable_presence = true + + -- We need to modify the hash based on the constraints, otherwise it might + -- falsly identify a matching container when it has different constraints + for constr in presence_node:iterate_subtree(SEDS.presence_constraint_filter) do + final_size:flavor("PRESENCE" .. tostring(constr.resolved_range)) + end + end end if (not resolved_range and off.type) then resolved_range = off.type.resolved_range @@ -338,6 +315,10 @@ local function resolve_container_datatype_size(node) off.resolved_range = resolved_range end + if (variable_presence) then + final_size:setvariable(true) + end + if (special_fields) then -- Special field(s) present - this forces the is_packed property to be false final_size:setpack("OTHER") diff --git a/tool/scripts/25-seds_resolve_constraints.lua b/tool/scripts/25-seds_resolve_constraints.lua index 5d52b47..58ccbea 100644 --- a/tool/scripts/25-seds_resolve_constraints.lua +++ b/tool/scripts/25-seds_resolve_constraints.lua @@ -30,9 +30,250 @@ -- ------------------------------------------------------------------------- SEDS.info ("SEDS resolve constraints START") -local entitylist = {} +local weights = {} +local base_entitylist = {} +local base_entitymap = {} + +-- -------------------------------------------------------- +-- Helper function: convert rangesegment values into comparable numbers +-- +-- The "resolved_range" within the DOM is usually an instance of +-- the rangesegment userdata type, or it could be a string if +-- the subject entity is an enumeration. +-- The goal here is to map everything to a number so it can be +-- properly compared to other numbers in a decision tree +-- -------------------------------------------------------- +local function remap_segment(refval,node) + + local orig = refval + + local reftype = node.resolved_entry and node.resolved_entry[#node.resolved_entry].type + + if (type(refval) == "userdata" and refval.is_single_value) then + refval = refval.as_single_value + end + + -- In the case that the entity datatype is an enumeration and the value + -- is a string, then it must be an enumeration label. In this case it + -- must be mapped back to the numeric value before adding it to the value table. + if (reftype and reftype.entity_type == "ENUMERATION_DATATYPE" and + type(refval) == "string") then + for label in reftype:iterate_subtree("ENUMERATION_ENTRY") do + if (label.name == refval) then + refval = label.value + break + end + end + + -- If value is still a string, that means the mapping did not exist + if (type(refval) == "string") then + node:error("value is undefined",refval) + end + end + + return refval + +end + +-- -------------------------------------------------------- +-- Helper function: resolve "entry" refs on constraint attributes +-- -------------------------------------------------------- +local function resolve_container_entity(node, cont) + if (not node.resolved_entry and node.attributes.entry) then + node.resolved_entry = cont:find_entity(node.attributes.entry) + end + if (not node.resolved_entry) then + node:error("undefined entry attribute",node.attributes.entry) + end + return node.resolved_entry +end + +-- -------------------------------------------------------- +-- Helper function: iterate over constraints on a container or element +-- -------------------------------------------------------- +local function iterate_constraints(elem) + + local results = {} + local iter = 0 + local parent_container + local filter + + if (elem.entity_type == "PRESENT_WHEN") then + filter = SEDS.presence_constraint_filter + elseif (SEDS.container_filter(elem)) then + parent_container = elem + filter = SEDS.constraint_filter + end + + if (not parent_container) then + parent_container = elem:find_parent(SEDS.container_filter) + end + + local my_iter = elem:iterate_subtree(filter) + + return function () + local node = my_iter() + if (node) then + resolve_container_entity(node, parent_container) + return parent_container, node + end + end +end + +-- -------------------------------------------------------- +-- Helper function: iterate all values in a constraint +-- If the constraint is just a single value, the iterator returns just this value +-- -------------------------------------------------------- +local function iterate_resolvedrange_values(value) + + local value_set = {} + local iter_pos = 0 + local vforeach + + if (type(value) == "userdata") then -- seds_range typically + vforeach = value.foreach + end + if (type(vforeach) ~= "function") then + -- passthrough + vforeach = function(v,f) f(v) end + end + vforeach(value, function (segment) + value_set[1 + #value_set] = segment + end) + + return function() + iter_pos = 1 + iter_pos + return value_set[iter_pos] + end +end + + +-- -------------------------------------------------------- +-- Helper function: build a map to find derivative types +-- +-- This assembles the constraints on this type into a lookup +-- table that gets stored in the base type. + +-- The lookup table is 2 deep with constrained entities at +-- the first level and the required value(s) they may have +-- at the second level, ending with a reference to this node +-- -------------------------------------------------------- +local function build_derivative_decisiontree_map(cont) + + local basetype = cont.basetype + local entitymap = {} + local cont_entitylist = {} + + for _,node in iterate_constraints(cont) do + local refent = node.attributes.entry + if (entitymap[refent]) then + node:error("Duplicate constraint", entitymap[refent]) + else + entitymap[refent] = node + cont_entitylist[1 + #cont_entitylist] = refent + end + end + + -- Add to basetype decision map + local decisiontree_entry = basetype.derivative_decisiontree_map + + -- If basetype decision map did not exist, create now + if (not decisiontree_entry) then + decisiontree_entry = {} + basetype.derivative_decisiontree_map = decisiontree_entry + end + + -- process constraints in consistent order + table.sort(cont_entitylist, function(a,b) + local cweights = weights[basetype] + if (cweights[a] == cweights[b]) then + return (a < b) -- go by entity name if weights are equal + else + return (cweights[a] > cweights[b]) -- go by weight (highest first) + end + end) + + for _,entity in ipairs(cont_entitylist) do + local node = entitymap[entity] + local refval = node.resolved_range + + if (not decisiontree_entry.entities) then + decisiontree_entry.entities = {} + end + decisiontree_entry = decisiontree_entry.entities + + if (not decisiontree_entry[entity]) then + decisiontree_entry[entity] = {} + end + decisiontree_entry = decisiontree_entry[entity] + + if (not decisiontree_entry.refnode) then + decisiontree_entry.refnode = node + end + if (not decisiontree_entry.values) then + decisiontree_entry.values = SEDS.new_rangemap() + end + decisiontree_entry = decisiontree_entry.values + + if (not decisiontree_entry[refval]) then + decisiontree_entry[refval] = {} + end + + decisiontree_entry = decisiontree_entry[refval] + end + + if (not decisiontree_entry.derivatives) then + decisiontree_entry.derivatives = {} + end + decisiontree_entry = decisiontree_entry.derivatives + decisiontree_entry[1 + #decisiontree_entry] = cont + + return entitymap +end + +-- -------------------------------------------------------- +-- Helper function: resolve and expand references to values in the decision tree +-- +-- The values in the tree are directly as they were in the XML, meaning that +-- (a) refs to enum labels are still expressed as strings, not numbers +-- (b) multi-part ranges are in the table as a single constraint +-- +-- It is important to boil everything down to individual numbers as much as +-- possible because a later stage is going to generate a lookup sequence that +-- implements a binary search. This requires that all entries are "sortable" +-- and also that the sorting is done based on the real values, not strings. +-- -------------------------------------------------------- +local fixup_descisiontree_entities +fixup_descisiontree_entities = function (etree) + for entity in pairs(etree) do + local vtree = etree[entity].values + local refnode = etree[entity].refnode --- Pass 1: determine the various offsets and comparison values + if (vtree) then + local remapped_values = {} + for value,subtree in pairs(vtree) do + + -- Recursively process all entities at this level + if (subtree.entities) then + fixup_descisiontree_entities(subtree.entities) + end + + -- Now go through each of the values at this level and expand the set + for segment in iterate_resolvedrange_values(value) do + local v = remap_segment(segment,refnode) + remapped_values[v] = subtree + end + end + etree[entity].values = remapped_values + end + end +end + +-- -------------------------------------------------- +-- MAIN CODE BEGINS HERE +-- -------------------------------------------------- + +-- Initial Pass: determine the various offsets and comparison values -- for every constraint in the database. -- This is used to determine the order of nodes in the decision -- tree that is produced. By putting the common values first @@ -41,25 +282,18 @@ local entitylist = {} for cont in SEDS.root:iterate_subtree(SEDS.container_filter) do local basetype = cont.basetype if (basetype) then - if (not entitylist[basetype]) then - entitylist[basetype] = {} + if (not base_entitylist[basetype]) then + base_entitylist[basetype] = {} end - for node in cont:iterate_subtree(SEDS.constraint_filter) do - if (node.attributes.entry) then - node.resolved_entry = cont:find_entity(node.attributes.entry) - end - if (not node.resolved_entry) then - node:error("undefined entry attribute",node.attributes.entry) - else - local refval = node.attributes.value - local refent = node.attributes.entry - local el = entitylist[basetype] - if (not el[refent]) then - el[refent] = {} - end - el = el[refent] - el[refval] = (el[refval] or 0) + 1 + for _,node in iterate_constraints(cont) do + local refent = node.attributes.entry + local refval = tostring(node.resolved_range) + local el = base_entitylist[basetype] + if (not el[refent]) then + el[refent] = {} end + el = el[refent] + el[refval] = (el[refval] or 0) + 1 end end end @@ -69,8 +303,7 @@ end -- of the number of total derivatives that have a constraint -- on that offset to the number of different possible values -- The higher this number is, the earlier it should be checked. -local weights = {} -for cont,el in pairs(entitylist) do +for cont,el in pairs(base_entitylist) do weights[cont] = {} for ent,vtbl in pairs(el) do local total = 0 @@ -83,104 +316,51 @@ for cont,el in pairs(entitylist) do end end - +-- Main Pass: Find all containers and process the constraints within them for cont in SEDS.root:iterate_subtree(SEDS.container_filter) do - local basetype = cont.basetype - local entitymap = {} - local entitylist = {} - - if (basetype) then - - for node in cont:iterate_subtree(SEDS.constraint_filter) do - if (node.attributes.entry) then - node.resolved_entry = cont:find_entity(node.attributes.entry) + -- Check for "PresentWhen" elements and resolve those constraints + for entity in cont:iterate_subtree(SEDS.container_entry_filter) do + local presence = entity:find_first("PRESENT_WHEN") + if (presence) then + local condition_list = {} + if (entity.presence_condition_list) then + entity:error("entity already has condition list") end - if (not node.resolved_entry) then - node:error("undefined entry attribute",node.attributes.entry) - else - local refent = node.attributes.entry - if (entitymap[refent]) then - node:error("Duplicate constraint", entitymap[refent]) - else - entitymap[refent] = node - entitylist[1 + #entitylist] = refent + for _,pcond in iterate_constraints(presence) do + local value_set = {} + for segment in iterate_resolvedrange_values(pcond.resolved_range) do + value_set[1 + #value_set] = remap_segment(segment,pcond) end + condition_list[1 + #condition_list] = { + entity_name = pcond.attributes.entry or "what", + entity = pcond.resolved_entry, + value_set = value_set + } end + entity.presence_condition_list = condition_list end + end - -- Add to basetype decision map - local decisiontree_entry = basetype.derivative_decisiontree_map - - -- If basetype decision map did not exist, create now - if (not decisiontree_entry) then - decisiontree_entry = {} - basetype.derivative_decisiontree_map = decisiontree_entry - end - - -- process constraints in consistent order - table.sort(entitylist, function(a,b) - local cweights = weights[basetype] - if (cweights[a] == cweights[b]) then - return (a < b) -- go by entity name if weights are equal - else - return (cweights[a] > cweights[b]) -- go by weight (highest first) - end - end) - - for _,entity in ipairs(entitylist) do - local node = entitymap[entity] - local refval = node.attributes.value - local reftype = node.resolved_entry and node.resolved_entry[#node.resolved_entry].type - - -- In the case that the entity datatype is an enumeration and the value - -- is a string, then it must be an enumeration label. In this case it - -- must be mapped back to the numeric value before adding it to the value table. - if (reftype and reftype.entity_type == "ENUMERATION_DATATYPE" and - type(refval) == "string") then - for label in reftype:iterate_subtree("ENUMERATION_ENTRY") do - if (label.name == refval) then - refval = label.value - break - end - end - - -- If value is still a string, that means the mapping did not exist - if (type(refval) == "string") then - node:error("value is undefined",refval) - end - end - - - if (not decisiontree_entry.entities) then - decisiontree_entry.entities = {} - end - decisiontree_entry = decisiontree_entry.entities - - if (not decisiontree_entry[entity]) then - decisiontree_entry[entity] = {} - end - decisiontree_entry = decisiontree_entry[entity] - - if (not decisiontree_entry.values) then - decisiontree_entry.values = {} - end - decisiontree_entry = decisiontree_entry.values - - if (not decisiontree_entry[refval]) then - decisiontree_entry[refval] = {} - end + -- If it is a derived container (i.e. has a base type) then add this + -- to the decision tree on the base type + if (cont.basetype) then + base_entitymap[cont] = build_derivative_decisiontree_map(cont) + end +end - decisiontree_entry = decisiontree_entry[refval] - end +-- Final Pass: cleanup +-- Expand all the multi-segment entries in the decisiontree into their constituent segments +-- This also handles things like mapping enumerated label references to their real value +for cont in SEDS.root:iterate_subtree(SEDS.container_filter) do - if (not decisiontree_entry.derivatives) then - decisiontree_entry.derivatives = {} + if (cont.derivative_decisiontree_map) then + local constraint_entity_set = cont.derivative_decisiontree_map.entities + if (constraint_entity_set) then + fixup_descisiontree_entities(constraint_entity_set) end - decisiontree_entry = decisiontree_entry.derivatives - decisiontree_entry[1 + #decisiontree_entry] = cont - end + end diff --git a/tool/scripts/30-seds_resolve_components.lua b/tool/scripts/30-seds_resolve_components.lua index 64b5151..8a5cca4 100644 --- a/tool/scripts/30-seds_resolve_components.lua +++ b/tool/scripts/30-seds_resolve_components.lua @@ -55,9 +55,8 @@ for rule in SEDS.root:iterate_subtree("INSTANCE_RULE") do if (pattern == "singleton") then -- Singletons should have a "component" attribute that identifies the component to instantiate -- As the pattern suggests, only a single one will be created, and it will be created now. - if (not rule.component) then - rule:error("invalid singleton component") - else + -- Just ignore it if the component is not defined + if (rule.component) then pending_instance_list[1 + #pending_instance_list] = SEDS.new_component_instance(rule.component, rule) end diff --git a/tool/src/seds_global.h b/tool/src/seds_global.h index a7b4cb2..3eeaa0e 100644 --- a/tool/src/seds_global.h +++ b/tool/src/seds_global.h @@ -32,7 +32,7 @@ * * That being said, this should still be portable to any other system out there * that implements basic POSIX/XOpen standards (Cygwin, OSX, BSD) and has a Lua - * version 5.2 interpreter available. + * version 5.3 interpreter available. */ #ifndef _SEDS_GLOBAL_H_ @@ -57,10 +57,6 @@ #include #include -/* compatibility shim to support compilation with Lua5.1 */ -#include "edslib_lua51_compatibility.h" - - /* * When using GNU C, it can give helpful warnings about mismatched arguments to printf-style calls. * this macro enables that error checking. @@ -153,4 +149,3 @@ extern seds_toplevel_t sedstool; #endif /* _SEDS_GLOBAL_H_ */ - diff --git a/tool/src/seds_memreq.c b/tool/src/seds_memreq.c index 42893a7..42c6c85 100644 --- a/tool/src/seds_memreq.c +++ b/tool/src/seds_memreq.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "seds_memreq.h" @@ -102,6 +103,11 @@ static int seds_memreq_add(lua_State *lua) psize->packing_status = SEDS_BYTEPACK_STATUS_OTHER; } + if (padd->is_variable_size) + { + psize->is_variable_size = true; + } + start_offset_bits = psize->raw_bit_size; start_offset_bytes = (psize->endpoint_bytes + padd->local_align_mask) & ~padd->local_align_mask; psize->endpoint_bytes = start_offset_bytes + padd->local_storage_bytes; @@ -145,6 +151,7 @@ static int seds_memreq_union(lua_State *lua) if (padd->raw_bit_size > psize->raw_bit_size) { psize->raw_bit_size = padd->raw_bit_size; + psize->is_variable_size = padd->is_variable_size; } psize->packing_status = SEDS_BYTEPACK_STATUS_OTHER; @@ -277,6 +284,18 @@ static int seds_memreq_setpack(lua_State *lua) return 0; } +/** + * Lua-callable function to set if an object is variably sized + * + */ +static int seds_memreq_setvariable(lua_State *lua) +{ + seds_memreq_t *psize = luaL_checkudata(lua, 1, "seds_memreq"); + + psize->is_variable_size = lua_toboolean(lua, 2); + return 0; +} + /** * Lua callable function to add a "pad" to an object size * @@ -339,6 +358,12 @@ static int seds_memreq_get_property(lua_State *lua) return 1; } + if (strcmp(lua_tostring(lua, 2), "is_variable") == 0) + { + lua_pushboolean(lua, psize->is_variable_size); + return 1; + } + if (strcmp(lua_tostring(lua, 2), "is_packed") == 0) { if ((8 * psize->local_storage_bytes) != psize->raw_bit_size) @@ -403,6 +428,12 @@ static int seds_memreq_get_property(lua_State *lua) return 1; } + if (strcmp(lua_tostring(lua, 2), "setvariable") == 0) + { + lua_pushcfunction(lua, seds_memreq_setvariable); + return 1; + } + if (strcmp(lua_tostring(lua, 2), "pad") == 0) { lua_pushcfunction(lua, seds_memreq_pad); @@ -529,12 +560,115 @@ int seds_memreq_new_object(lua_State *lua) return 1; } +unsigned int seds_get_binary_digits(double limit, bool twos_complement) +{ + unsigned int digits; + double val; + int exp; + + val = frexp(fabs(limit), &exp); + if (isinf(val)) + { + /* Infinite is not really supported, just use max bits */ + digits = 64; + } + else if (isnormal(val) && val != 0.0 && exp >= 1) /* This can return NaN if the input was NaN */ + { + digits = exp; + + /* + * Because numbers are internally "double" values in Lua, + * anything above ~52 bits may result in an extra + * digit due to rounding up. This adds a "slop factor" + * to avoid triggering false errors (the cost is that + * this might not flag a borderline case that is an error) + */ + if (digits > 52 && val < 0.55) + { + --digits; + } + else if (twos_complement && val == 0.5 && limit < 0.0) + { + /* special handling for twos complement, has one extra + * value of range on the negative side */ + --digits; + } + } + else + { + digits = 1; + } + + return digits; +} + +/** + * Lua-callable function to calculate the number of digits required for a value + * + * This requires some math functions that were deprecated in Lua 5.4 so this + * implementation is written in C. + * + * Input Lua stack: + * 1: Maximum Value of data type + * 2: Minimum Value of data type + * 3: Encoding type (optional) + */ +int seds_calulate_range_digits(lua_State *lua) +{ + double max = lua_tonumber(lua, 1); + double min = lua_tonumber(lua, 2); + double posdig; + double negdig; + bool twos_complement; + + if (lua_type(lua, 3) == LUA_TSTRING) + { + twos_complement = (strcmp(lua_tostring(lua, 3), "twoscomplement") == 0); + } + else + { + /* By default assume twos complement */ + twos_complement = true; + } + + /* determine number of digits in positive range */ + if (max > 0) + { + posdig = seds_get_binary_digits(max, twos_complement); + } + else + { + posdig = 0; + } + + /* determine number of digits in negative range */ + if (min < 0) + { + negdig = seds_get_binary_digits(min, twos_complement); + } + else + { + negdig = 0; + } + + /* If both positive and negative values are representable + * then this needs a sign bit, so add 1 to both sides */ + if (posdig > 0 && negdig > 0) + { + ++posdig; + ++negdig; + } + + lua_pushinteger(lua, posdig); + lua_pushinteger(lua, negdig); + return 2; +} + /*******************************************************************************/ /* Externally-Called Functions */ /* (referenced outside this unit and prototyped in a separate header) */ /*******************************************************************************/ - /* * ------------------------------------------------------ * External API function - see full details in prototype. @@ -545,5 +679,6 @@ void seds_memreq_register_globals(lua_State *lua) luaL_checktype(lua, -1, LUA_TTABLE); lua_pushcfunction(lua, seds_memreq_new_object); lua_setfield(lua, -2, "new_size_object"); - + lua_pushcfunction(lua, seds_calulate_range_digits); + lua_setfield(lua, -2, "calulate_range_digits"); } diff --git a/tool/src/seds_memreq.h b/tool/src/seds_memreq.h index b8067f6..120993a 100644 --- a/tool/src/seds_memreq.h +++ b/tool/src/seds_memreq.h @@ -89,6 +89,7 @@ typedef struct seds_integer_t local_align_mask; /**< Expected alignment requirements based on typical alignment rules */ seds_bytepack_status_t packing_status; /**< If the structure is packed efficiently, this allows for some added optimizations */ seds_checksum_t checksum; /**< Checksum/Hash value for the data type definition */ + seds_boolean_t is_variable_size; /**< Set true if the size is not a fixed quantity of bits */ } seds_memreq_t; @@ -105,4 +106,3 @@ void seds_memreq_register_globals(lua_State *lua); #endif /* _SEDS_MEMREQ_H_ */ - diff --git a/tool/src/seds_outputfile.c b/tool/src/seds_outputfile.c index 3fbe224..5323b32 100644 --- a/tool/src/seds_outputfile.c +++ b/tool/src/seds_outputfile.c @@ -552,7 +552,11 @@ void seds_output_file_open(seds_output_file_t *pfile, const char *basedir, const seds_output_file_close(pfile); - if (mkdir(basedir, 0755) < 0 && errno != EEXIST) + if (basedir == NULL) + { + basedir = "."; + } + else if (mkdir(basedir, 0755) < 0 && errno != EEXIST) { SEDS_REPORT_ERRNO(FATAL, basedir); } @@ -785,6 +789,42 @@ static int seds_lua_output_file_set_method(lua_State *lua) return 0; } +/* ------------------------------------------------------------------- */ +/** + * Get definition from the lua environment with fallback + * + * Attempts to call SEDS.get_define() with the given varname, and leaves + * the result on the top of the stack. This adjust the stack such that + * one (and only one) item is added, and it is either the result from + * get_define or the fallback string. + * + */ +static void seds_get_define_safe(lua_State *lua, const char *varname, const char *fallback) +{ + int stack_top = lua_gettop(lua); + + lua_getglobal(lua, "SEDS"); + if (lua_type(lua, -1) == LUA_TTABLE) + { + lua_getfield(lua, -1, "get_define"); + if (lua_type(lua, -1) == LUA_TFUNCTION) + { + lua_pushstring(lua, varname); + lua_call(lua, 1, 1); + } + } + + /* numbers or strings are allowed as a return value */ + if (lua_type(lua, -1) != LUA_TNUMBER && lua_type(lua, -1) != LUA_TSTRING) + { + lua_pushstring(lua, fallback); + } + + /* Put the result at the former stack top and remove intermediates */ + lua_replace(lua, stack_top + 1); + lua_settop(lua, stack_top + 1); +} + /* ------------------------------------------------------------------- */ /** * Lua callable file open function @@ -880,27 +920,14 @@ static int seds_lua_output_file_open(lua_State *lua) if (inp != destfile && tolower((int)*inp) == 'h') { /* Must be a header file - put into "inc" subdir */ - subdir = "inc"; + seds_get_define_safe(lua, "INCDIR", "inc"); /* Set up for the guard macro */ - lua_getglobal(lua, "SEDS"); - if (lua_type(lua, -1) == LUA_TTABLE) - { - lua_getfield(lua, -1, "get_define"); - if (lua_type(lua, -1) == LUA_TFUNCTION) - { - lua_pushstring(lua, "MISSION_NAME"); - lua_call(lua, 1, 1); - if (lua_type(lua, -1) == LUA_TSTRING) - { - lua_pushstring(lua, "_"); - lua_concat(lua, 2); - strncpy(pfile->cheader_guard_string, lua_tostring(lua, -1), sizeof(pfile->cheader_guard_string) - 1); - pfile->cheader_guard_string[sizeof(pfile->cheader_guard_string) - 1] = 0; - } - } - lua_pop(lua, 1); - } + seds_get_define_safe(lua, "EDSTOOL_PROJECT_NAME", "EDS"); + lua_pushstring(lua, "_"); + lua_concat(lua, 2); + strncpy(pfile->cheader_guard_string, lua_tostring(lua, -1), sizeof(pfile->cheader_guard_string) - 1); + pfile->cheader_guard_string[sizeof(pfile->cheader_guard_string) - 1] = 0; lua_pop(lua, 1); inp = destfile; @@ -926,9 +953,11 @@ static int seds_lua_output_file_open(lua_State *lua) else { /* Must be a source file - put into "src" subdir */ - subdir = "src"; + seds_get_define_safe(lua, "SRCDIR", "src"); } + subdir = lua_tostring(lua, -1); + /* * Set up C-style comment blocks */ @@ -978,7 +1007,7 @@ static int seds_lua_output_file_open(lua_State *lua) } lua_rawgetp(lua, LUA_REGISTRYINDEX, &sedstool.GLOBAL_SYMBOL_TABLE_KEY); - lua_getfield(lua, -1, "MISSION_BINARY_DIR"); + lua_getfield(lua, -1, "EDSTOOL_OUTPUT_DIR"); seds_output_file_open(pfile, lua_tostring(lua, -1), subdir, destfile, sourcefile); diff --git a/tool/src/seds_preprocess.c b/tool/src/seds_preprocess.c index 3768ffd..074852d 100644 --- a/tool/src/seds_preprocess.c +++ b/tool/src/seds_preprocess.c @@ -272,11 +272,14 @@ static int seds_resolve_luafy_seds_reference(lua_State *lua) const char *p, *q; seds_boolean_t quoting_required; - luaL_buffinit(lua, &buffer); - luaL_addstring(&buffer, "return "); input = lua_tostring(lua, -1); quoting_required = seds_resolve_is_lua_quoting_required(input); + /* Note - As of Lua 5.4, this may push something onto the stack. + * Whereas in 5.3 and below, it does not change the stack. */ + luaL_buffinit(lua, &buffer); + luaL_addstring(&buffer, "return "); + if (quoting_required) { /* @@ -411,26 +414,6 @@ static int seds_resolve_luafy_seds_reference(lua_State *lua) return 1; } - -#if (LUA_VERSION_NUM <= 501) - -/* in lua <= 5.1, the environment is set after the load */ -static int seds_resolve_load_references(lua_State *lua) -{ - seds_resolve_luafy_seds_reference(lua); - - lua_pushnil(lua); - if (luaL_loadstring(lua, lua_tostring(lua, -2)) == LUA_OK) - { - lua_rawgetp(lua, LUA_REGISTRYINDEX, &SEDS_PREPROCESS_ENVIRONMENT); - lua_setfenv(lua, -2); - lua_pushnil(lua); - } - return 2; -} - -#else - /* in lua >= 5.2, the environment is set prior to load via LUA_RIDX_GLOBALS */ static int seds_resolve_load_references(lua_State *lua) { @@ -455,9 +438,6 @@ static int seds_resolve_load_references(lua_State *lua) return 2; } -#endif - - static int seds_resolve_set_eval_func(lua_State *lua) { lua_settop(lua, 1); diff --git a/tool/src/seds_range.c b/tool/src/seds_range.c new file mode 100644 index 0000000..4488730 --- /dev/null +++ b/tool/src/seds_range.c @@ -0,0 +1,2453 @@ +/* + * LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation + * + * Copyright (c) 2020 United States Government as represented by + * the Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file seds_range.c + * \ingroup tool + * \author joseph.p.hickey@nasa.gov + * + * Implementation of the "range" construct from EDS, + * allowing it to be used as if it was a value. + */ + +#include +#include +#include +#include +#include + +#include "seds_range.h" + +typedef enum +{ + seds_range_check_reject = 0, + seds_range_check_accept = 1, + seds_range_check_is_less = 2, + seds_range_check_is_greater = 3, +} seds_range_check_t; + +/*******************************************************************************/ +/* Internal / static Helper Functions */ +/* (these are not referenced outside this unit) */ +/*******************************************************************************/ + +static seds_rangesegment_t *seds_rangesegment_push(lua_State *lua, seds_rangesegment_t *pinit); +static void seds_range_push(lua_State *lua, seds_rangecontent_t initial_content); + +/** + * Helper function: call routine for every segment within range + * Range must be in stack position indicated by range_idx + * The routine to call should be at the top of the stack (-1) + * + * NOTE: This does NOT pop the routine, it leaves it on the stack (different from lua_call here) + */ +static int seds_range_foreach_segment_impl(lua_State *lua, int range_idx); + +/** + * Helper function: initalize a limit (min or max) from a lua value + * at the given index, which should be absolute + * + * If the index is invalid or none, this will initialize the limit + * as infinite + * + * table data should have the form: + * { value = X, inclusive = Y } + */ +int seds_rangesegment_set_limit_from_idx(lua_State *lua, int lim_idx, seds_rangelimit_t *rlim) +{ + if (lua_istable(lua, lim_idx)) + { + lua_pushstring(lua, "value"); + lua_gettable(lua, lim_idx); + } + else if (lua_isnumber(lua, lim_idx)) + { + lua_pushvalue(lua, lim_idx); + } + else + { + /* initialize to no limit */ + lua_pushnil(lua); + } + + rlim->is_defined = !lua_isnil(lua, -1); + if (rlim->is_defined) + { + rlim->valx = lua_tonumber(lua, -1); + } + lua_pop(lua, 1); + + /* determine if it is inclusive or not */ + /* assume it is inclusive unless specifically indicated as false */ + if (lua_istable(lua, lim_idx)) + { + lua_pushstring(lua, "inclusive"); + lua_gettable(lua, lim_idx); + } + else + { + /* initialize to default */ + lua_pushnil(lua); + } + + /* nil here means it was not specified, so interpret it as true */ + rlim->is_inclusive = lua_isnil(lua, -1) || lua_toboolean(lua, -1); + lua_pop(lua, 1); + + return 0; +} + +/** + * Helper function: Push a table onto the stack that represents + * the limit in table form + * + * table data will have the form: + * { value = X, inclusive = Y } + */ +int seds_rangesegment_push_limit_value(lua_State *lua, seds_rangelimit_t *rlim) +{ + double whole; + + if (!rlim->is_defined) + { + lua_pushnil(lua); + } + else if (modf(rlim->valx, &whole) == 0.0) + { + /* The limit is a whole number, so push it as an integer */ + lua_pushinteger(lua, (lua_Integer)whole); + } + else + { + lua_pushnumber(lua, rlim->valx); + } + + return 1; +} + +/** + * Helper function: Push a table onto the stack that represents + * the limit in table form + * + * table data will have the form: + * { value = X, inclusive = Y } + */ +int seds_rangesegment_get_limit_as_table(lua_State *lua, seds_rangelimit_t *rlim) +{ + /* check if it is a normal limit */ + if (rlim->is_defined) + { + lua_newtable(lua); + + lua_pushstring(lua, "value"); + seds_rangesegment_push_limit_value(lua, rlim); + lua_settable(lua, -3); + + lua_pushstring(lua, "inclusive"); + lua_pushboolean(lua, rlim->is_inclusive); + lua_settable(lua, -3); + } + else + { + /* it has no limit */ + lua_pushnil(lua); + } + + return 1; +} + +/** + * Helper function: gets the range type as a string + */ +int seds_rangesegment_get_type_as_string(lua_State *lua, seds_rangesegment_t *pseg) +{ + const char *inclusion; + + inclusion = NULL; + + if (pseg->max.is_defined && pseg->min.is_defined) + { + if (pseg->min.is_inclusive) + { + if (pseg->max.is_inclusive) + { + inclusion = "inclusiveMinInclusiveMax"; + } + else + { + inclusion = "inclusiveMinExclusiveMax"; + } + } + else + { + if (pseg->max.is_inclusive) + { + inclusion = "exclusiveMinInclusiveMax"; + } + else + { + inclusion = "exclusiveMinExclusiveMax"; + } + } + } + else if (pseg->max.is_defined) + { + if (pseg->max.is_inclusive) + { + inclusion = "atMost"; + } + else + { + inclusion = "lessThan"; + } + } + else if (pseg->min.is_defined) + { + if (pseg->min.is_inclusive) + { + inclusion = "atLeast"; + } + else + { + inclusion = "greaterThan"; + } + } + else + { + inclusion = "infinite"; + } + + lua_pushstring(lua, inclusion); + return 1; +} + +/** + * Helper function: tests if lim1 may include lim2, considering exclusivity + */ +static seds_range_check_t seds_rangesegment_compare(const seds_rangelimit_t *plim1, const seds_rangelimit_t *plim2) +{ + seds_range_check_t retval; + + if (!plim1->is_defined) + { + /* if lim1 is not defined then it will match anything EXCEPT + * if lim2 is also not defined. */ + if (plim2->is_defined || plim1->is_inclusive) + { + retval = seds_range_check_accept; + } + else + { + retval = seds_range_check_reject; + } + } + else if (!plim2->is_defined) + { + /* lim1 is defined so lim2 must also be defined */ + retval = seds_range_check_reject; + } + else if (plim1->valx < plim2->valx) + { + /* both are defined and lim1 < lim2 */ + retval = seds_range_check_is_less; + } + else if (plim1->valx > plim2->valx) + { + /* both are defined and lim1 > lim2 */ + retval = seds_range_check_is_greater; + } + else if (!plim2->is_inclusive || plim1->is_inclusive) + { + /* values are equal: if lim2 is exclusive, then it + * matches regardless of lim1 */ + retval = seds_range_check_accept; + } + else + { + retval = seds_range_check_reject; + } + + return retval; +} + +/** + * Helper function: tests if lim1 >= lim2, considering exclusivity + */ +static bool seds_rangesegment_compare_max(const seds_rangelimit_t *plim1, const seds_rangelimit_t *plim2) +{ + seds_range_check_t cmp = seds_rangesegment_compare(plim1, plim2); + + return (cmp == seds_range_check_accept || cmp == seds_range_check_is_greater); +} + +/** + * Helper function: tests if lim1 <= lim2, considering exclusivity + */ +static bool seds_rangesegment_compare_min(const seds_rangelimit_t *plim1, const seds_rangelimit_t *plim2) +{ + seds_range_check_t cmp = seds_rangesegment_compare(plim1, plim2); + + return (cmp == seds_range_check_accept || cmp == seds_range_check_is_less); +} + +/** + * Helper function: tests if segment has ANY values in it + */ +static bool seds_rangesegment_is_empty(const seds_rangesegment_t *pseg) +{ + /* if either side is undefined then the segment is not empty (it goes to infinity) */ + if (!pseg->max.is_defined || !pseg->min.is_defined) + { + return false; + } + + /* if min <= max then it is not empty */ + return !seds_rangesegment_compare_min(&pseg->min, &pseg->max); +} + +/** + * Helper function: check if the rangesegment is a singular value + */ +static bool seds_rangesegment_is_single_value(const seds_rangesegment_t *pseg) +{ + /* if either side is undefined then the segment is not a single value (it goes to infinity) */ + /* similarly if either side is not inclusive, it cannot be a single value either */ + if (!pseg->max.is_defined || !pseg->min.is_defined || !pseg->max.is_inclusive || !pseg->min.is_inclusive) + { + return false; + } + + /* it is a single value if min and max are the same */ + return (pseg->max.valx == pseg->min.valx); +} + +/** + * Helper function: push a single value onto the stack corresponding to the segment + * + * If the segment is a single value, this pushes that value in simple/primitive form. + * For example a single number is pushed as a number. If the value is not actually + * a single, then this depends on the "force" option. + * + * If force is false - then push nil + * If force is true - then push a number representing the midpoint of the segment + */ +static void seds_rangesegment_push_as_single_value(lua_State *lua, int arg_idx, bool force) +{ + const seds_rangesegment_t *pseg = luaL_testudata(lua, arg_idx, "seds_rangesegment"); + + if (pseg == NULL) + { + /* if its not a rangesegment, then the value is the arg itself */ + lua_pushvalue(lua, arg_idx); + } + else if (seds_rangesegment_is_single_value(pseg)) + { + /* if it is a single number, then push that number (min or max is the same) */ + lua_pushnumber(lua, pseg->max.valx); + } + else if (force) + { + /* push the midpoint as a number, this is reasonable for sorting purposes */ + lua_pushnumber(lua, (pseg->min.valx + pseg->max.valx) / 2); + } + else + { + /* not able to push as single value, push as nil */ + lua_pushnil(lua); + } +} + +/** + * Helper function: Sets the rangelimit to given value + */ +static void seds_rangelimit_set_value(seds_rangelimit_t *plim, seds_number_t value, bool inclusive) +{ + plim->is_defined = true; + plim->is_inclusive = inclusive; + plim->valx = value; +} + +/** + * Helper function: Sets the segment to a single value + * This is a range that only matches one specific value + */ +static void seds_rangesegment_set_single_value(seds_rangesegment_t *pseg, seds_number_t value) +{ + seds_rangelimit_set_value(&pseg->min, value, true); + pseg->max = pseg->min; +} + +/** + * Helper function: Sets the segment to an empty set + * This is a range that contains no valid values + */ +static void seds_rangesegment_set_empty(seds_rangesegment_t *pseg) +{ + seds_rangelimit_set_value(&pseg->min, 0, false); + pseg->max = pseg->min; +} + +/** + * Helper function: tests if seg2 is totally within seg1 + */ +static bool seds_rangesegment_includes_rangesegment(seds_rangesegment_t *pseg1, seds_rangesegment_t *pseg2) +{ + return seds_rangesegment_compare_min(&pseg1->min, &pseg2->min) && + seds_rangesegment_compare_max(&pseg1->max, &pseg2->max); +} + +/** + * Helper function to check if an argument permits range segment processing + * This applies to anything numeric + */ +static bool seds_rangesegment_acceptable_value(lua_State *lua, int arg_idx) +{ + return (lua_isnumber(lua, arg_idx) || luaL_testudata(lua, arg_idx, "seds_rangesegment")); +} + +/** + * Helper function: get argument as a segment + * + * If the given lua stack argument is a rangesegment, then return a pointer to it directly + * If it is a single value, then initialize the temp to refer to that single value and return + * + * Either way the result is a rangesegment that can be passed to other routines. + */ +static seds_rangesegment_t *seds_rangesegment_get_safe(lua_State *lua, int arg_pos, seds_rangesegment_t *tempbuf) +{ + seds_rangesegment_t *pseg = luaL_testudata(lua, arg_pos, "seds_rangesegment"); + + if (pseg == NULL) + { + if (lua_isnumber(lua, arg_pos)) + { + seds_rangesegment_set_single_value(tempbuf, lua_tonumber(lua, arg_pos)); + } + else if (lua_isnoneornil(lua, arg_pos)) + { + /* this handles e.g. nil, consider it an empty set */ + seds_rangesegment_set_empty(tempbuf); + } + else + { + luaL_error(lua, "Cannot get numeric rangesegment from type=%s", luaL_typename(lua, arg_pos)); + } + + pseg = tempbuf; + } + + return pseg; +} + +/** + * Helper function to check if a rangesegment matches a given value + * + * Expected Args: + * 1: Object to check + * + * Upvalues: + * 1: pointer to boolean with result + * 2: Reference Segment to check against + * + * If the argument (1) is NOT covered by the reference segment, sets the + * boolean to false. + */ +static int seds_rangesegment_match_helper(lua_State *lua) +{ + seds_rangesegment_t *pseg = luaL_checkudata(lua, lua_upvalueindex(2), "seds_rangesegment"); + seds_rangesegment_t *pcheck; + seds_rangesegment_t temp; + bool *presult = lua_touserdata(lua, lua_upvalueindex(1)); + + /* this should only set result to false, never set it to true */ + if (seds_rangesegment_acceptable_value(lua, 1)) + { + pcheck = seds_rangesegment_get_safe(lua, 1, &temp); + if (!seds_rangesegment_includes_rangesegment(pseg, pcheck)) + { + *presult = false; + } + } + else + { + *presult = false; + } + + return 0; +} + +/** + * Lua callable function to check if the argument falls within the range + * + * Expected Stack args: + * 1: rangesegment object + * 2: value to check (number) or another rangesegment + */ +static int seds_rangesegment_matches(lua_State *lua) +{ + bool result; + + result = true; + + lua_pushlightuserdata(lua, &result); + lua_pushvalue(lua, 1); + lua_pushcclosure(lua, seds_rangesegment_match_helper, 2); + + if (luaL_testudata(lua, 2, "seds_range")) + { + /* call the helper with each segment */ + seds_range_foreach_segment_impl(lua, 2); + lua_pop(lua, 1); + } + else + { + /* just call the helper directly with the arg */ + lua_pushvalue(lua, 2); + lua_call(lua, 1, 0); + } + + lua_pushboolean(lua, result); + return 1; +} + +/** + * Helper function to compute a intersection of 2 segments + * + * NOTE: this will produce an invalid result if the segments + * do not overlap at all (this can be checked for via is_empty) + */ +static void seds_rangesegment_compute_intersection(seds_rangesegment_t *pcombined, const seds_rangesegment_t *pseg1, + const seds_rangesegment_t *pseg2) +{ + memset(pcombined, 0, sizeof(*pcombined)); + + /* get the greater of the two minimums */ + if (seds_rangesegment_compare_min(&pseg1->min, &pseg2->min)) + { + /* this means r1 min <= r2 min */ + pcombined->min = pseg2->min; + } + else + { + pcombined->min = pseg1->min; + } + + /* get the lesser of the two maximums */ + if (seds_rangesegment_compare_max(&pseg1->max, &pseg2->max)) + { + /* this means r1 max >= r2 max */ + pcombined->max = pseg2->max; + } + else + { + pcombined->max = pseg1->max; + } +} + +/** + * Helper function to bridge 2 segments together + * + * NOTE: this covers any gap between them, the caller should confirm the segments + * are overlapping in some way before bridging if the intent is to create a union + */ +static void seds_rangesegment_compute_bridge(seds_rangesegment_t *pcombined, const seds_rangesegment_t *pseg1, + const seds_rangesegment_t *pseg2) +{ + /* get the lesser of the two minimums */ + if (seds_rangesegment_compare_min(&pseg1->min, &pseg2->min)) + { + /* this means r1 min <= r2 min */ + pcombined->min = pseg1->min; + } + else + { + pcombined->min = pseg2->min; + } + + /* get the greater of the two maximums */ + if (seds_rangesegment_compare_max(&pseg1->max, &pseg2->max)) + { + /* this means r1 max >= r2 max */ + pcombined->max = pseg1->max; + } + else + { + pcombined->max = pseg2->max; + } +} + +/** + * Lua-callable function to create a new range userdata object + * + * Expected input Lua stack, direct initializer: + * 1: value + */ +int seds_rangesegment_new_single_value(lua_State *lua) +{ + seds_rangesegment_t *pseg; + + pseg = seds_rangesegment_push(lua, NULL); + + if (lua_isnumber(lua, 1)) + { + seds_rangesegment_set_single_value(pseg, lua_tonumber(lua, 1)); + } + else + { + /* consider it an empty set */ + seds_rangesegment_set_empty(pseg); + } + + return 1; +} + +/** + * Lua callable function to calculate the intersection of 2 range segments + * + * Expected Stack args: + * 1: object 1, may be a rangesegment OR a single value + * 2: object 2, may be another rangesegment OR a single value + */ +static int seds_rangesegment_intersection(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp1; + seds_rangesegment_t temp2; + seds_rangesegment_t combined; + + pseg1 = seds_rangesegment_get_safe(lua, 1, &temp1); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + seds_rangesegment_compute_intersection(&combined, pseg1, pseg2); + + if (seds_rangesegment_is_empty(&combined)) + { + /* the result is empty, return nothing */ + return 0; + } + + if (seds_rangesegment_is_single_value(&combined)) + { + /* the result is a single number, return it */ + lua_pushnumber(lua, combined.min.valx); + } + else + { + /* the result is a valid segment, return it */ + seds_rangesegment_push(lua, &combined); + } + + return 1; +} + +/** + * Helper function: tests if there is a gap between two limit values + */ +static bool seds_rangelimit_is_contiguous(seds_rangelimit_t *plim1, seds_rangelimit_t *plim2) +{ + bool result; + + if (plim1->is_defined && plim2->is_defined && plim1->valx == plim2->valx) + { + /* as long as one or othe other is inclusive, it is contiguous */ + result = (plim1->is_inclusive || plim2->is_inclusive); + } + else + { + result = false; + } + + return result; +} + +/** + * Lua callable function to check for continuity between two segments + * + * Expected Stack args: + * 1: object 1, may be a rangesegment OR a single value + * 2: object 2, may be another rangesegment OR a single value + */ +static int seds_rangesegment_contiguous_with(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp1; + seds_rangesegment_t temp2; + bool result; + + pseg1 = seds_rangesegment_get_safe(lua, 1, &temp1); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + result = (seds_rangelimit_is_contiguous(&pseg1->max, &pseg2->min) || + seds_rangelimit_is_contiguous(&pseg1->min, &pseg2->max)); + + lua_pushboolean(lua, result); + + return 1; +} + +static bool seds_rangesegment_compute_union(seds_rangesegment_t *pcombined, seds_rangesegment_t *pseg1, + seds_rangesegment_t *pseg2) +{ + seds_rangesegment_t temp; + bool result; + + /* first compute the intersection, if this is not empty this + * means the union will be a single segment */ + seds_rangesegment_compute_intersection(&temp, pseg1, pseg2); + + /* if the intersection was empty, there is still a possibility that + * the segment(s) are immediately next to eachother even if they do not + * overlap. In this case the union is still representable as a single + * segment. */ + if (!seds_rangesegment_is_empty(&temp) || seds_rangelimit_is_contiguous(&pseg1->max, &pseg2->min) || + seds_rangelimit_is_contiguous(&pseg1->min, &pseg2->max)) + { + seds_rangesegment_compute_bridge(pcombined, pseg1, pseg2); + result = true; + } + else + { + result = false; + } + + return result; +} + +/** + * Lua callable function to compute the union of two ranges + * + * Expected Stack args: + * 1: object 1, may be a rangesegment OR a single value + * 2: object 2, may be another rangesegment OR a single value + * + * If the two segments are contiguous, this returns a segment + * that reflects the union of the two. If they are not contiguous + * this returns nil - this means the union cannot be expressed + * as a single segment, it is disjointed. + */ +static int seds_rangesegment_union(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp1; + seds_rangesegment_t temp2; + seds_rangesegment_t combined; + + pseg1 = seds_rangesegment_get_safe(lua, 1, &temp1); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + if (!seds_rangesegment_compute_union(&combined, pseg1, pseg2)) + { + /* not representable as a single segment */ + return 0; + } + + if (seds_rangesegment_is_single_value(&combined)) + { + /* the result is a single number, return it */ + lua_pushnumber(lua, combined.min.valx); + } + else + { + /* the result is a valid segment, return it */ + seds_rangesegment_push(lua, &combined); + } + + return 1; +} + +/** + * Lua callable function to get a range object property + * + * Expected Stack args: + * 1: range object + * 2: property key + */ +static int seds_rangesegment_get_property(lua_State *lua) +{ + seds_rangesegment_t *pseg = luaL_checkudata(lua, 1, "seds_rangesegment"); + const char *key; + + /* + * Handle some fixed key names, but only if the key is a string + */ + if (lua_isstring(lua, 2)) + { + key = lua_tostring(lua, 2); + + if (strcmp(key, "is_single_value") == 0) + { + lua_pushboolean(lua, seds_rangesegment_is_single_value(pseg)); + return 1; + } + + if (strcmp(key, "as_single_value") == 0) + { + seds_rangesegment_push_as_single_value(lua, 1, true); + return 1; + } + + if (strcmp(key, "matches") == 0) + { + lua_pushcfunction(lua, seds_rangesegment_matches); + return 1; + } + + if (strcmp(key, "intersection") == 0) + { + lua_pushcfunction(lua, seds_rangesegment_intersection); + return 1; + } + + if (strcmp(key, "union") == 0) + { + lua_pushcfunction(lua, seds_rangesegment_union); + return 1; + } + + if (strcmp(key, "empty") == 0) + { + lua_pushboolean(lua, seds_rangesegment_is_empty(pseg)); + return 1; + } + + if (strcmp(key, "contiguous_with") == 0) + { + lua_pushcfunction(lua, seds_rangesegment_contiguous_with); + return 1; + } + + if (strcmp(key, "type") == 0) + { + /* return a string with the range type */ + return seds_rangesegment_get_type_as_string(lua, pseg); + } + + if (strcmp(key, "min") == 0) + { + /* return as a 2-tuple with value and inclusion */ + return seds_rangesegment_get_limit_as_table(lua, &pseg->min); + } + + if (strcmp(key, "max") == 0) + { + /* return as a 2-tuple with value and inclusion */ + return seds_rangesegment_get_limit_as_table(lua, &pseg->max); + } + + if (strcmp(key, "min_value") == 0) + { + return seds_rangesegment_push_limit_value(lua, &pseg->min); + } + + if (strcmp(key, "max_value") == 0) + { + return seds_rangesegment_push_limit_value(lua, &pseg->max); + } + + if (strcmp(key, "min_inclusive") == 0) + { + lua_pushboolean(lua, pseg->min.is_inclusive); + return 1; + } + + if (strcmp(key, "max_inclusive") == 0) + { + lua_pushboolean(lua, pseg->max.is_inclusive); + return 1; + } + } + + return 0; +} + +/** + * Lua callable function to get a range object property + * + * Expected Stack args: + * 1: range object + * 2: property key + * 3: property value + */ +static int seds_rangesegment_set_property(lua_State *lua) +{ + seds_rangesegment_t *pseg = luaL_checkudata(lua, 1, "seds_rangesegment"); + const char *key; + + /* + * Handle some fixed key names, but only if the key is a string + */ + if (lua_isstring(lua, 2)) + { + key = lua_tostring(lua, 2); + + if (strcmp(key, "min") == 0) + { + return seds_rangesegment_set_limit_from_idx(lua, 3, &pseg->min); + } + + if (strcmp(key, "max") == 0) + { + return seds_rangesegment_set_limit_from_idx(lua, 3, &pseg->max); + } + + if (strcmp(key, "min_value") == 0) + { + pseg->min.is_defined = !lua_isnil(lua, 3); + if (pseg->min.is_defined) + { + pseg->min.valx = lua_tonumber(lua, 3); + } + return 0; + } + + if (strcmp(key, "max_value") == 0) + { + pseg->max.is_defined = !lua_isnil(lua, 3); + if (pseg->max.is_defined) + { + pseg->max.valx = lua_tonumber(lua, 3); + } + return 0; + } + + if (strcmp(key, "min_inclusive") == 0) + { + pseg->min.is_inclusive = lua_toboolean(lua, 3); + return 0; + } + + if (strcmp(key, "max_inclusive") == 0) + { + pseg->max.is_inclusive = lua_toboolean(lua, 3); + return 0; + } + } + + return 0; +} + +/** + * Lua callable function to convert a range object to a string + * + * This implements the "tostring" method for range objects, and it + * returns a summary string containing the bits, bytes, alignment, + * and checksum of the object binary format. + */ +static int seds_rangesegment_to_string(lua_State *lua) +{ + seds_rangesegment_t *pseg = luaL_checkudata(lua, 1, "seds_rangesegment"); + char minbuf[28]; + char maxbuf[28]; + char stringbuf[64]; + + if (pseg->min.is_defined) + { + snprintf(minbuf, sizeof(minbuf), "%.1lf", (double)pseg->min.valx); + } + else + { + snprintf(minbuf, sizeof(minbuf), "-inf"); + } + if (pseg->max.is_defined) + { + snprintf(maxbuf, sizeof(maxbuf), "%.1lf", (double)pseg->max.valx); + } + else + { + snprintf(maxbuf, sizeof(maxbuf), "+inf"); + } + + snprintf(stringbuf, sizeof(stringbuf), "%c%s,%s%c", pseg->min.is_inclusive ? '[' : '(', minbuf, maxbuf, + pseg->max.is_inclusive ? ']' : ')'); + + lua_pushstring(lua, stringbuf); + return 1; +} + +/** + * Lua callable function to check for range equality + * + * Expected Stack args: + * 1: range object + * 2: range object + * + */ +static int seds_rangesegment_is_equal(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp2; + + pseg1 = luaL_checkudata(lua, 1, "seds_rangesegment"); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + bool result; + + if (pseg1 == pseg2) + { + /* they are the same object */ + result = true; + } + else + { + result = (memcmp(pseg1, pseg2, sizeof(seds_rangesegment_t)) == 0); + } + + lua_pushboolean(lua, result); + return 1; +} + +/** + * Compare two rangesegments for sorting purposes + * + * This facilitates organizing segments by relative values, but it only makes sense + * if the segments do not overlap at all. + * + * Return value is like strcmp() + */ +static int seds_rangesegment_sort_compare(const seds_rangesegment_t *pseg1, const seds_rangesegment_t *pseg2) +{ + int result; + seds_rangesegment_t isect_temp; + + /* compute the intersection - this must be empty */ + seds_rangesegment_compute_intersection(&isect_temp, pseg1, pseg2); + if (!seds_rangesegment_is_empty(&isect_temp)) + { + /* they intersect, so consider it equal for value-checking purposes */ + result = 0; + } + else if (seds_rangesegment_compare_min(&pseg1->min, &pseg2->min)) + { + /* pseg1 < pseg2 */ + result = -1; + } + else + { + result = 1; + } + + return result; +} + +/** + * Lua callable function to compare rangesegments + * + * "Less than" does not apply directly to ranges, the main need here + * is to be consistent in application of whatever comparison is done, + * such that when e.g. sorting the resulting order will be consistent + * from run to run. + * + * Expected Stack args: + * 1: rangesegment object + * 2: any numeric value + * + */ +static int seds_rangesegment_is_lessthan(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp2; + + pseg1 = luaL_checkudata(lua, 1, "seds_rangesegment"); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + lua_pushboolean(lua, seds_rangesegment_sort_compare(pseg1, pseg2) < 0); + return 1; +} + +/** + * Lua callable function to compare rangesegments + * + * "Less than" does not apply directly to ranges, the main need here + * is to be consistent in application of whatever comparison is done, + * such that when e.g. sorting the resulting order will be consistent + * from run to run. + * + * Expected Stack args: + * 1: range object + * 2: range object + * + */ +static int seds_rangesegment_is_lessequal(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp2; + + pseg1 = luaL_checkudata(lua, 1, "seds_rangesegment"); + pseg2 = seds_rangesegment_get_safe(lua, 2, &temp2); + + lua_pushboolean(lua, seds_rangesegment_sort_compare(pseg1, pseg2) <= 0); + return 1; +} + +/** + * Lua-callable function to create a new range userdata object + * + * Expected input Lua stack, table initializer: + * 1: table with data in the form: + * { min = { value = X, inclusive = Y }, max = { value = X, inclusive = Y } } + * + * Optionally min and max can be direct values, inclusivity will be assumed true + */ +int seds_rangesegment_new_from_table(lua_State *lua) +{ + seds_rangesegment_t *pseg = seds_rangesegment_push(lua, NULL); + + lua_pushstring(lua, "min"); + lua_gettable(lua, 1); + seds_rangesegment_set_limit_from_idx(lua, lua_gettop(lua), &pseg->min); + lua_pop(lua, 1); + + lua_pushstring(lua, "max"); + lua_gettable(lua, 1); + seds_rangesegment_set_limit_from_idx(lua, lua_gettop(lua), &pseg->max); + lua_pop(lua, 1); + + return 1; +} + +/** + * Lua-callable function to create a copy of a range userdata object + * + * Argument 1 must be another instance of seds_rangesegment + */ +int seds_rangesegment_new_copy(lua_State *lua) +{ + seds_rangesegment_push(lua, luaL_checkudata(lua, 1, "seds_rangesegment")); + return 1; +} + +/** + * Lua-callable function to create a new range userdata object + * + * Expected input Lua stack, direct initializer: + * 1: minimum value (number or nil) + * 2: maximum value (number or nil) + * 3: inclusion mode (string per EDS spec, or nil) + */ +int seds_rangesegment_new_from_direct(lua_State *lua) +{ + seds_rangesegment_t *pseg; + const char *inclusion; + + lua_settop(lua, 3); /* in case args were ommitted, assume nil */ + + pseg = seds_rangesegment_push(lua, NULL); + + /* this will only get the values */ + seds_rangesegment_set_limit_from_idx(lua, 1, &pseg->min); + seds_rangesegment_set_limit_from_idx(lua, 2, &pseg->max); + + inclusion = luaL_optstring(lua, 3, NULL); + if (inclusion != NULL) + { + if (strcasecmp(inclusion, "inclusiveMinInclusiveMax") == 0) + { + pseg->min.is_inclusive = true; + pseg->max.is_inclusive = true; + } + else if (strcasecmp(inclusion, "inclusiveMinExclusiveMax") == 0) + { + pseg->min.is_inclusive = true; + pseg->max.is_inclusive = false; + } + else if (strcasecmp(inclusion, "exclusiveMinInclusiveMax") == 0) + { + pseg->min.is_inclusive = false; + pseg->max.is_inclusive = true; + } + else if (strcasecmp(inclusion, "exclusiveMinExclusiveMax") == 0) + { + pseg->min.is_inclusive = false; + pseg->max.is_inclusive = false; + } + else if (strcasecmp(inclusion, "greaterThan") == 0) + { + pseg->min.is_inclusive = false; + pseg->max.is_defined = false; + } + else if (strcasecmp(inclusion, "atLeast") == 0) + { + pseg->min.is_inclusive = true; + pseg->max.is_defined = false; + } + else if (strcasecmp(inclusion, "lessThan") == 0) + { + pseg->min.is_defined = false; + pseg->max.is_inclusive = false; + } + else if (strcasecmp(inclusion, "atMost") == 0) + { + pseg->min.is_defined = false; + pseg->max.is_inclusive = true; + } + else + { + return luaL_error(lua, "Invalid rangeType: %s", inclusion); + } + } + + return 1; +} + +/** + * Lua-callable function to create a new range userdata object + * + * Depending on the argument this will initialize the range object in one of 3 ways: + * - table init + * - copy init + * - direct init + * + * The decision is made based on the type of the first arg + */ +int seds_rangesegment_new(lua_State *lua) +{ + int nret; + + if (lua_gettop(lua) != 1) + { + /* multi-arg construction: assume it is a direct initializer with a min and max */ + nret = seds_rangesegment_new_from_direct(lua); + } + else if (lua_type(lua, 1) == LUA_TTABLE) + { + /* table constructor form */ + nret = seds_rangesegment_new_from_table(lua); + } + else if (lua_type(lua, 1) == LUA_TUSERDATA) + { + /* copy constructor form */ + nret = seds_rangesegment_new_copy(lua); + } + else + { + /* single value constructor form */ + nret = seds_rangesegment_new_single_value(lua); + } + + return nret; +} + +/** + * Helper function: Pushes a userdata object of seds_rangesegment_t onto the stack + */ +seds_rangesegment_t *seds_rangesegment_push(lua_State *lua, seds_rangesegment_t *pinit) +{ + seds_rangesegment_t *pseg = lua_newuserdata(lua, sizeof(seds_rangesegment_t)); + + memset(pseg, 0, sizeof(*pseg)); + if (luaL_newmetatable(lua, "seds_rangesegment")) + { + lua_pushstring(lua, "__index"); + lua_pushcfunction(lua, seds_rangesegment_get_property); + lua_rawset(lua, -3); + lua_pushstring(lua, "__newindex"); + lua_pushcfunction(lua, seds_rangesegment_set_property); + lua_rawset(lua, -3); + lua_pushstring(lua, "__tostring"); + lua_pushcfunction(lua, seds_rangesegment_to_string); + lua_rawset(lua, -3); + lua_pushstring(lua, "__eq"); + lua_pushcfunction(lua, seds_rangesegment_is_equal); + lua_rawset(lua, -3); + lua_pushstring(lua, "__lt"); + lua_pushcfunction(lua, seds_rangesegment_is_lessthan); + lua_rawset(lua, -3); + lua_pushstring(lua, "__le"); + lua_pushcfunction(lua, seds_rangesegment_is_lessequal); + lua_rawset(lua, -3); + } + lua_setmetatable(lua, -2); + + if (pinit != NULL) + { + /* copy constructor form */ + memcpy(pseg, pinit, sizeof(*pseg)); + } + else + { + memset(pseg, 0, sizeof(*pseg)); + } + + return pseg; +} + +/** + * Helper function: Check for an empty range + * + * The range must be on the stack + */ +static bool seds_range_is_empty(lua_State *lua, int range_idx) +{ + seds_range_ud_t *prange = luaL_testudata(lua, range_idx, "seds_range"); + return (prange != NULL && prange->content == SEDS_RANGECONTENT_EMPTY); +} + +/** + * Helper function: Check for an infinite range + * + * The range must be on the stack + */ +static bool seds_range_is_infinite(lua_State *lua, int range_idx) +{ + seds_range_ud_t *prange = luaL_testudata(lua, range_idx, "seds_range"); + return (prange != NULL && prange->content == SEDS_RANGECONTENT_INFINITE); +} + +/** + * Helper function: Check for a normal range + * + * The range must be on the stack. A normal range is one that has at least + * one real segment; it is not empty nor infinite. + */ +static bool seds_range_is_normal(lua_State *lua, int range_idx) +{ + seds_range_ud_t *prange = luaL_testudata(lua, range_idx, "seds_range"); + return (prange != NULL && prange->content == SEDS_RANGECONTENT_NORMAL); +} + +static void seds_range_set_normal(lua_State *lua, int range_idx, int content_idx) +{ + seds_range_ud_t *prange = luaL_checkudata(lua, range_idx, "seds_range"); + prange->content = SEDS_RANGECONTENT_NORMAL; + lua_pushvalue(lua, content_idx); + lua_setuservalue(lua, range_idx); +} + +static void seds_range_set_infinite(lua_State *lua, int range_idx) +{ + seds_range_ud_t *prange = luaL_checkudata(lua, range_idx, "seds_range"); + prange->content = SEDS_RANGECONTENT_INFINITE; + lua_pushnil(lua); + lua_setuservalue(lua, range_idx); +} + +static void seds_range_set_empty(lua_State *lua, int range_idx) +{ + seds_range_ud_t *prange = luaL_checkudata(lua, range_idx, "seds_range"); + prange->content = SEDS_RANGECONTENT_EMPTY; + lua_pushnil(lua); + lua_setuservalue(lua, range_idx); +} + +/** + * Helper function: Call a routine for every segment within the range + * + * The range and the function must be on the stack + */ +static int seds_range_foreach_segment_impl(lua_State *lua, int range_idx) +{ + int func_idx; + int nsegs; + + nsegs = 0; + + if (seds_range_is_normal(lua, range_idx)) + { + func_idx = lua_gettop(lua); /* function to call should be at top */ + + lua_getuservalue(lua, range_idx); + if (lua_istable(lua, -1)) + { + /* multi-segment range; loop through all segments */ + lua_pushnil(lua); + while (lua_next(lua, -2) != 0) + { + /* do not care about the value, the key is the segment */ + lua_pop(lua, 1); + + lua_pushvalue(lua, func_idx); + lua_pushvalue(lua, -2); + lua_call(lua, 1, 0); + + ++nsegs; + } + } + else + { + /* single-segment range; just call the function */ + lua_pushvalue(lua, func_idx); + lua_pushvalue(lua, -2); + lua_call(lua, 1, 0); + + ++nsegs; + } + + /* reset stack to original position, removes anything pushed here */ + lua_settop(lua, func_idx); + } + + return nsegs; +} + +/** + * Lua-callable Helper function to attempt to merge/unionize segments + * within a range + * + * Expected input stack: + * 1: segment to unionize (seds_rangesegment userdata or a single number) + * + * Upvalues: + * 1: subject rangesegment + */ +static int seds_range_try_merge_segments(lua_State *lua) +{ + seds_rangesegment_t *pseg1; + seds_rangesegment_t *pseg2; + seds_rangesegment_t temp2; + seds_rangesegment_t combined; + + lua_settop(lua, 1); + pseg1 = luaL_checkudata(lua, lua_upvalueindex(1), "seds_rangesegment"); + pseg2 = seds_rangesegment_get_safe(lua, 1, &temp2); + + if (seds_rangesegment_compute_union(&combined, pseg1, pseg2)) + { + /* replace the original segment with the combined one */ + *pseg1 = combined; + } + + return 0; +} + +/** + * Lua-callable Helper function to filter overlapping segments + * + * Expected input stack: + * 1: value to test (seds_rangesegment userdata or a single number) + * + * Upvalues: + * 1: Table to add non-overlapping results into + * 2: value to compare against + */ +static int seds_range_check_overlap(lua_State *lua) +{ + seds_rangesegment_t *prefseg; + seds_rangesegment_t *pcheckseg; + seds_rangesegment_t temp1; + seds_rangesegment_t temp2; + int ref_idx = lua_upvalueindex(2); + bool is_overlap; + + /* simplest case, if the values are exactly equal, they overlap */ + is_overlap = lua_compare(lua, 1, ref_idx, LUA_OPEQ); + if (!is_overlap && seds_rangesegment_acceptable_value(lua, 1) && seds_rangesegment_acceptable_value(lua, ref_idx)) + { + prefseg = seds_rangesegment_get_safe(lua, ref_idx, &temp1); + pcheckseg = seds_rangesegment_get_safe(lua, 1, &temp2); + + is_overlap = seds_rangesegment_includes_rangesegment(prefseg, pcheckseg); + } + + /* if it completely overlaps reference, do nothing. otherwise this is a different value + * and needs to be added to the result set */ + if (!is_overlap) + { + /* disjointed from reference, add it to table */ + lua_pushboolean(lua, true); + lua_rawset(lua, lua_upvalueindex(1)); + } + + return 0; +} + +/** + * Lua-callable Helper function to append a segment into another range + * + * Expected input stack: + * 1: segment to add (seds_rangesegment userdata or a single number) + * + * Upvalues: + * 1: Dest range (seds_range userdata) + * + * NOTE: this modifies the destination (upvalue 1) in-place. + */ + +static int seds_range_append_segment(lua_State *lua) +{ + seds_rangesegment_t *pmerged; + seds_rangesegment_t temp_segment; + int range_idx = lua_upvalueindex(1); + int result_idx; + + lua_settop(lua, 1); + + /* check for the simple cases */ + if (seds_range_is_infinite(lua, range_idx)) + { + /* nothing to do, the dest is already infinite */ + return 0; + } + + /* range segments need to be merged */ + /* special handling for numeric segments, they can be combined */ + if (seds_rangesegment_acceptable_value(lua, 1)) + { + /* first merge with anything it is contiguous with */ + /* index 2 is a NEW segment based on arg 1 (always a segment, not a number) */ + pmerged = seds_rangesegment_push(lua, seds_rangesegment_get_safe(lua, 1, &temp_segment)); + lua_pushvalue(lua, 2); + lua_pushcclosure(lua, seds_range_try_merge_segments, 1); + seds_range_foreach_segment_impl(lua, range_idx); + lua_pop(lua, 1); /* remove seds_range_try_merge_segments */ + + /* push the new segment onto the stack - if the new segment is a singular value, push that */ + if (seds_rangesegment_is_single_value(pmerged)) + { + lua_pushnumber(lua, pmerged->min.valx); + } + else + { + lua_pushvalue(lua, 2); /* keep it as a segment */ + } + } + else + { + /* the value we are adding is itself */ + lua_pushvalue(lua, 1); + } + + lua_newtable(lua); + result_idx = lua_gettop(lua); /* this is our temporary result table */ + + lua_pushvalue(lua, result_idx); + lua_pushvalue(lua, 2); + lua_pushcclosure(lua, seds_range_check_overlap, 2); + seds_range_foreach_segment_impl(lua, range_idx); + lua_pop(lua, 1); /* remove seds_range_check_overlap */ + + /* now check if the result table is empty */ + lua_pushvalue(lua, 2); + lua_pushnil(lua); + if (lua_next(lua, result_idx)) + { + /* drop key and value, does not matter, this only means its not empty */ + lua_pop(lua, 2); + + /* add the segment to the table (key is on top already) */ + lua_pushboolean(lua, true); + lua_rawset(lua, result_idx); + } + else + { + /* replace the original result_idx (table) with this single value */ + lua_replace(lua, result_idx); + } + + /* this new item becomes the userdata of the range */ + seds_range_set_normal(lua, range_idx, result_idx); + + return 0; +} + +/** + * Lua callable function to append all segments from one range into another range, + * creating the union of the two ranges + * + * Expected Stack args: + * 1: range object + * 2: range to add in to (1) + * + * A new range object is returned containing a new set of segments, this + * does not modify the input args + */ +static int seds_range_union(lua_State *lua) +{ + int arg_idx; + int result_idx; + + seds_range_push(lua, SEDS_RANGECONTENT_EMPTY); /* make a NEW range object to return, do not modify the original */ + result_idx = lua_gettop(lua); + + lua_pushvalue(lua, result_idx); + lua_pushcclosure(lua, seds_range_append_segment, 1); + + for (arg_idx = 1; arg_idx < result_idx && !seds_range_is_infinite(lua, result_idx); ++arg_idx) + { + if (luaL_testudata(lua, arg_idx, "seds_range")) + { + if (seds_range_is_infinite(lua, arg_idx)) + { + seds_range_set_infinite(lua, result_idx); + } + else if (!seds_range_is_empty(lua, arg_idx)) + { + seds_range_foreach_segment_impl(lua, arg_idx); + } + } + else if (!lua_isnil(lua, arg_idx)) + { + /* just call the append function with the original arg */ + /* need to push the append function again because lua_call removes it, + * and we might need it for the next iteration of the loop */ + lua_pushvalue(lua, result_idx + 1); + lua_pushvalue(lua, arg_idx); + lua_call(lua, 1, 0); + } + } + + /* return the destination range, allows for chaining */ + lua_settop(lua, result_idx); + return 1; +} + +/** + * Lua callable function to check if a range matches a value + * + * Expected Stack args: + * 1: segment to check (can also be number) + * + * Upvalues: + * 1: light user data pointing to boolean result + * 2: reference value + */ +static int seds_range_check_segmentmatch(lua_State *lua) +{ + int *pmatchcount; + int refval_idx; + bool localresult; + + pmatchcount = lua_touserdata(lua, lua_upvalueindex(1)); + refval_idx = lua_upvalueindex(2); + + /* if the reference and the test value are equal, move on */ + localresult = lua_compare(lua, 1, refval_idx, LUA_OPEQ); + if (!localresult && luaL_testudata(lua, 1, "seds_rangesegment") != NULL) + { + lua_pushcfunction(lua, seds_rangesegment_matches); + lua_pushvalue(lua, 1); + lua_pushvalue(lua, refval_idx); + lua_call(lua, 2, 1); + + localresult = lua_toboolean(lua, -1); + lua_pop(lua, 1); + } + + if (localresult) + { + /* consider it a match */ + ++(*pmatchcount); + } + + return 0; +} + +/** + * Lua callable function to check if a range matches a segment or value + * + * Expected Stack args: + * 1: range object + * 2: value to check (may be a value or a segment) + */ +static int seds_range_matches_segment(lua_State *lua) +{ + int required_segments; + int matching_segments; + + matching_segments = 0; + + if (seds_range_is_empty(lua, 1)) + { + /* will always be false */ + required_segments = 1; + } + else if (seds_range_is_infinite(lua, 1)) + { + /* will always be true */ + required_segments = 0; + } + else + { + /* need at least one positive match */ + required_segments = 1; + + lua_pushlightuserdata(lua, &matching_segments); + lua_pushvalue(lua, 2); + lua_pushcclosure(lua, seds_range_check_segmentmatch, 2); + + seds_range_foreach_segment_impl(lua, 1); + } + + lua_pushboolean(lua, matching_segments >= required_segments); + return 1; +} + +/** + * Lua callable function to check if a range matches a value + * + * Expected Stack args: + * 1: value to check (may be a value or a segment) + * + * Upvalues: + * 1: counter object (pointer to int as lightuserdata) + * 2: comparison range object + */ +static int seds_range_subsegment_match_helper(lua_State *lua) +{ + int *pmatchcount = lua_touserdata(lua, lua_upvalueindex(1)); + + lua_pushcfunction(lua, seds_range_matches_segment); + lua_pushvalue(lua, lua_upvalueindex(2)); + lua_pushvalue(lua, 1); + lua_call(lua, 2, 1); + + if (lua_toboolean(lua, -1)) + { + ++(*pmatchcount); + } + + return 0; +} + +/** + * Lua callable function to check if a range matches a value + * + * Expected Stack args: + * 1: range object + * 2: value to check (may be a value, a segment, or another range) + */ +static int seds_range_matches(lua_State *lua) +{ + int needed_segments; + int matching_segments; + + luaL_checkudata(lua, 1, "seds_range"); + lua_settop(lua, 2); /* ignore extra args */ + + matching_segments = 0; + + if (seds_range_is_empty(lua, 1)) + { + /* will always result as false */ + needed_segments = 1; + } + else if (seds_range_is_infinite(lua, 1)) + { + /* will always result as true */ + needed_segments = 0; + } + else + { + lua_pushlightuserdata(lua, &matching_segments); + lua_pushvalue(lua, 1); + lua_pushcclosure(lua, seds_range_subsegment_match_helper, 2); + + if (luaL_testudata(lua, 2, "seds_range")) + { + /* check all the segments from the second range */ + needed_segments = seds_range_foreach_segment_impl(lua, 2); + } + else + { + /* pass through the value to the helper */ + needed_segments = 1; + lua_pushvalue(lua, 2); + lua_call(lua, 1, 0); + } + + lua_settop(lua, 2); /* remove anything pushed above */ + } + + lua_pushboolean(lua, (matching_segments == needed_segments)); + return 1; +} + +/** + * Lua callable function to calculate a intersection + * + * Expected Stack args: + * 1: value to intersect (may be a value or a single segment, NOT another range) + * + * Upvalues: + * 1: function to call on resulting segment(s) + * 2: value to intersect (may be a value or a single segment, OR another range) + * + * NOTE: the subject value must not be an infinite range. The caller already + * checked for this possibility and handled that. This works with either an + * empty range or a normally-defined range. + */ +static int seds_range_subsegment_intersection_helper(lua_State *lua) +{ + int result_idx = lua_upvalueindex(1); + int subject_idx = lua_upvalueindex(2); + + if (luaL_testudata(lua, subject_idx, "seds_range")) + { + /* If intersecting with another range, need to do each segment of that range */ + /* we can recursively call this function to do this */ + lua_pushvalue(lua, result_idx); + lua_pushvalue(lua, 1); + lua_pushcclosure(lua, seds_range_subsegment_intersection_helper, 2); + seds_range_foreach_segment_impl(lua, subject_idx); + } + else + { + if (seds_rangesegment_acceptable_value(lua, 1) && seds_rangesegment_acceptable_value(lua, subject_idx)) + { + /* compute the intersection of the two numeric segments (either of which could be a single value) */ + lua_pushcfunction(lua, seds_rangesegment_intersection); + lua_pushvalue(lua, 1); + lua_pushvalue(lua, subject_idx); + lua_call(lua, 2, 1); + } + else if (seds_range_is_infinite(lua, subject_idx) || lua_compare(lua, subject_idx, 1, LUA_OPEQ)) + { + lua_pushvalue(lua, 1); + } + else + { + /* there is no intersection */ + lua_pushnil(lua); + } + + if (!lua_isnil(lua, -1)) + { + /* result is valid - call the function */ + lua_pushvalue(lua, result_idx); + lua_insert(lua, -2); + lua_call(lua, 1, 0); + } + } + return 0; +} + +/** + * Lua callable function to calculate the intersection of two ranges + * + * Expected Stack args: + * 1: range object + * 2: value to intersect with (may be a value, a segment, or another range) + */ +static int seds_range_intersection(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_range"); + + /* if no arg was given, this becomes a noop */ + if (lua_gettop(lua) == 1) + { + seds_range_push(lua, SEDS_RANGECONTENT_INFINITE); + } + + /* make sure the arg is another seds_range object */ + /* this makes the following logic more consistent as it always intersects two seds_range objects */ + lua_settop(lua, 2); + if (!luaL_testudata(lua, 2, "seds_range")) + { + /* arg is not natively a range - make a seds_range to wrap it */ + lua_pushcfunction(lua, seds_range_union); + lua_insert(lua, 2); + lua_call(lua, 1, 1); + } + + /* simple case if either range is infinite - then the result is equal to the other arg */ + if (seds_range_is_infinite(lua, 1)) + { + /* result is equivalent to arg2 */ + lua_pushvalue(lua, 2); + } + else if (seds_range_is_infinite(lua, 2)) + { + /* result is equivalent to arg1 */ + lua_pushvalue(lua, 1); + } + else + { + /* result will be a new range, make it initially empty */ + seds_range_push(lua, SEDS_RANGECONTENT_EMPTY); + + /* this computes the intersection of each segment + * and appends all the non-empty resulting segments to the result range */ + lua_pushvalue(lua, 3); + lua_pushcclosure(lua, seds_range_append_segment, 1); + lua_pushvalue(lua, 2); + lua_pushcclosure(lua, seds_range_subsegment_intersection_helper, 2); + + /* call the intersection helper for everything in THIS range */ + /* this in turn will invoke seds_range_append_segment for all overlapping segments */ + seds_range_foreach_segment_impl(lua, 1); + + lua_pop(lua, 1); /* remove the helper function */ + } + + /* return the result which was left at the top of the stack */ + return 1; +} + +/** + * Lua callable function to invoke a callback on all range segments + * + * Expected Stack args: + * 1: range object + * 2: function to invoke + */ +static int seds_range_foreach_segment(lua_State *lua) +{ + lua_settop(lua, 2); + seds_range_foreach_segment_impl(lua, 1); + return 0; +} + +/** + * Lua callable function to get a range object property + * + * Expected Stack args: + * 1: range object + * 2: property key + */ +static int seds_range_get_property(lua_State *lua) +{ + const char *key; + + luaL_checkudata(lua, 1, "seds_range"); + lua_settop(lua, 2); + + /* + * Handle some fixed key names, but only if the key is a string + */ + if (lua_isstring(lua, 2)) + { + key = lua_tostring(lua, 2); + + if (strcmp(key, "union") == 0) + { + lua_pushcfunction(lua, seds_range_union); + return 1; + } + + if (strcmp(key, "foreach") == 0) + { + lua_pushcfunction(lua, seds_range_foreach_segment); + return 1; + } + + if (strcmp(key, "matches") == 0) + { + lua_pushcfunction(lua, seds_range_matches); + return 1; + } + + if (strcmp(key, "intersection") == 0) + { + lua_pushcfunction(lua, seds_range_intersection); + return 1; + } + + if (strcmp(key, "empty") == 0) + { + lua_pushboolean(lua, seds_range_is_empty(lua, 1)); + return 1; + } + + if (strcmp(key, "infinite") == 0) + { + lua_pushboolean(lua, seds_range_is_infinite(lua, 1)); + return 1; + } + + /* if this is a singleton segment, pass it through */ + lua_getuservalue(lua, 1); + if (luaL_testudata(lua, -1, "seds_rangesegment")) + { + lua_replace(lua, 1); + return seds_rangesegment_get_property(lua); + } + + if (strcmp(key, "is_single_value") == 0) + { + lua_pushboolean(lua, lua_isnumber(lua, -1) || lua_isstring(lua, -1) || lua_isboolean(lua, -1)); + return 1; + } + + if (strcmp(key, "as_single_value") == 0) + { + if (lua_isnumber(lua, -1) || lua_isstring(lua, -1) || lua_isboolean(lua, -1)) + { + /* just return the value */ + } + else + { + lua_pushnil(lua); + } + return 1; + } + } + return 0; +} + +/** + * Lua callable function to set a range object property + * + * Expected Stack args: + * 1: range object + * 2: property key + * 3: property value + */ +static int seds_range_set_property(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_range"); + lua_settop(lua, 3); + + /* Currently the range object has no (writable) properties of its own */ + return 0; +} + +static int seds_range_tostring_helper(lua_State *lua) +{ + luaL_Buffer *pbuf = lua_touserdata(lua, lua_upvalueindex(1)); + + /* convert the value to a string and concat */ + luaL_addstring(pbuf, ","); + luaL_addstring(pbuf, luaL_tolstring(lua, 1, NULL)); + + return 0; +} + +/** + * Lua callable function to convert a range object to a string + * + * This implements the "tostring" method for range objects, and it + * returns a summary string containing the bits, bytes, alignment, + * and checksum of the object binary format. + */ +static int seds_range_to_string(lua_State *lua) +{ + luaL_Buffer lbuf; + + luaL_checkudata(lua, 1, "seds_range"); + + if (seds_range_is_empty(lua, 1)) + { + lua_pushstring(lua, "(empty)"); + } + else if (seds_range_is_infinite(lua, 1)) + { + lua_pushstring(lua, "[infinite]"); + } + else + { + luaL_buffinit(lua, &lbuf); + lua_pushlightuserdata(lua, &lbuf); + lua_pushcclosure(lua, seds_range_tostring_helper, 1); + + seds_range_foreach_segment_impl(lua, 1); + luaL_pushresult(&lbuf); + + /* chop off the leading comma */ + if (lua_rawlen(lua, -1) > 1) + { + lua_pushstring(lua, lua_tostring(lua, -1) + 1); + } + } + + return 1; +} + +/** + * Lua callable function to check for range equality + * + * Expected Stack args: + * 1: range object + * 2: range object + * + */ +static int seds_range_is_equal(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_range"); + luaL_checkudata(lua, 2, "seds_range"); + + /* cheat here, this is not terribly efficient + * call "matches" in both directions, if r1 matches r2 + * and r2 matches r1, then they must be equivalent. + * + * (if one was a subset of the other, one test would fail) + */ + + lua_pushcfunction(lua, seds_range_matches); + lua_pushvalue(lua, 1); + lua_pushvalue(lua, 2); + lua_call(lua, 2, 1); + if (lua_toboolean(lua, -1)) + { + lua_pop(lua, 1); + lua_pushcfunction(lua, seds_range_matches); + lua_pushvalue(lua, 2); + lua_pushvalue(lua, 1); + lua_call(lua, 2, 1); + } + + return 1; +} + +/** + * Lua callable function to compare ranges + * + * "Less than" does not apply directly to ranges, the main need here + * is to be consistent in application of whatever comparison is done, + * such that when e.g. sorting the resulting order will be consistent + * from run to run. + * + * Expected Stack args: + * 1: range object + * 2: range object + * + */ +static int seds_range_is_lessthan(lua_State *lua) +{ + /* simplest method is to convert both to strings, then return the result + * of that comparison */ + + luaL_tolstring(lua, 1, NULL); + luaL_tolstring(lua, 2, NULL); + lua_pushboolean(lua, lua_compare(lua, -1, -2, LUA_OPLT)); + return 1; +} + +/** + * Lua callable function to compare ranges + * + * "Less than" does not apply directly to ranges, the main need here + * is to be consistent in application of whatever comparison is done, + * such that when e.g. sorting the resulting order will be consistent + * from run to run. + * + * Expected Stack args: + * 1: range object + * 2: range object + * + */ +static int seds_range_is_lessequal(lua_State *lua) +{ + /* simplest method is to convert both to strings, then return the result + * of that comparison */ + + luaL_tolstring(lua, 1, NULL); + luaL_tolstring(lua, 2, NULL); + lua_pushboolean(lua, lua_compare(lua, -1, -2, LUA_OPLE)); + return 1; +} + +/** + * Helper function: Pushes a userdata object of seds_range_t onto the stack + * The new range will be empty + */ +static void seds_range_push(lua_State *lua, seds_rangecontent_t initial_content) +{ + seds_range_ud_t *prange = lua_newuserdata(lua, sizeof(seds_range_ud_t)); + + memset(prange, 0, sizeof(*prange)); + prange->content = initial_content; + if (luaL_newmetatable(lua, "seds_range")) + { + lua_pushstring(lua, "__index"); + lua_pushcfunction(lua, seds_range_get_property); + lua_rawset(lua, -3); + lua_pushstring(lua, "__newindex"); + lua_pushcfunction(lua, seds_range_set_property); + lua_rawset(lua, -3); + lua_pushstring(lua, "__tostring"); + lua_pushcfunction(lua, seds_range_to_string); + lua_rawset(lua, -3); + lua_pushstring(lua, "__eq"); + lua_pushcfunction(lua, seds_range_is_equal); + lua_rawset(lua, -3); + lua_pushstring(lua, "__lt"); + lua_pushcfunction(lua, seds_range_is_lessthan); + lua_rawset(lua, -3); + lua_pushstring(lua, "__le"); + lua_pushcfunction(lua, seds_range_is_lessequal); + lua_rawset(lua, -3); + } + lua_setmetatable(lua, -2); +} + +/** + * Helper function: Pushes a userdata object of seds_range_t onto the stack, + * and initializes it to the given arguments + * + * Upvalue 1 indicates whether the new range should be initially empty or infinite + */ +static int seds_range_new(lua_State *lua) +{ + if (lua_isnoneornil(lua, 1)) + { + /* by default if no args, create an infinite range */ + seds_range_push(lua, SEDS_RANGECONTENT_INFINITE); + + if (!lua_toboolean(lua, lua_upvalueindex(1))) + { + /* create an empty range if using the empty constructor */ + seds_range_set_empty(lua, -1); + } + } + else + { + /* if the argument is another range, this acts as a copy constructor */ + if (luaL_testudata(lua, 1, "seds_range")) + { + /* just in case - prune any other args */ + lua_settop(lua, 1); + } + else + { + /* for everything else make a segment object based on the arg(s) */ + /* this must be done first to pass-through the args correctly. */ + seds_rangesegment_new(lua); + } + + /* Call the union routine which returns a new range matching the new segment */ + lua_pushcfunction(lua, seds_range_union); + lua_insert(lua, -2); + + lua_call(lua, 1, 1); + } + + return 1; +} + +/** + * Helper function: Checks for a matching key within the rangemap + * If found: pushes two items on the stack - key + value - and returns true + * If not found: leaves the stack at its original position, and returns false + */ +static bool seds_rangemap_find_key(lua_State *lua, int map_idx, int arg_idx) +{ + bool found; + int key_idx; + + found = false; + + /* this requires walking the list, checking each range */ + lua_pushnil(lua); + key_idx = lua_gettop(lua); + while (lua_next(lua, map_idx)) + { + if (lua_isuserdata(lua, key_idx)) + { + /* invoke the "matches" metamethod */ + lua_getfield(lua, key_idx, "matches"); + if (lua_isfunction(lua, -1)) + { + lua_pushvalue(lua, key_idx); /* the range object */ + lua_pushvalue(lua, arg_idx); /* the argument */ + lua_call(lua, 2, 1); + + if (lua_toboolean(lua, -1)) + { + /* its a match - leave key + value on stack */ + lua_pop(lua, 1); + found = true; + break; + } + } + } + lua_settop(lua, key_idx); + } + + return found; +} + +/** + * Lua callable meta function: Gets a value from a rangemap + * This uses range-aware key matching + * + * Expected Stack args: + * 1: rangemap object + * 2: key value + */ +static int seds_rangemap_get_value(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_rangemap"); + lua_settop(lua, 2); + + if (!lua_isnil(lua, 2)) + { + /* first try a simple lookup from the table, for an exact match */ + lua_getuservalue(lua, 1); /* index 3 */ + lua_pushvalue(lua, 2); + lua_rawget(lua, 3); + + if (lua_isnil(lua, -1)) + { + /* + * need to try range checking: + * if this finds a matching key it pushes both key+value to the stack + * if it does not, it leaves the stack alone (so top will still be nil) + */ + seds_rangemap_find_key(lua, 3, 2); + } + } + + return 1; +} + +/** + * Lua callable meta function: Sets a value in a rangemap + * This uses range-aware key matching. If a matching key + * previously existed the value will be replaced with the new value + * + * Expected Stack args: + * 1: rangemap object + * 2: key value + * 3: value to set (may be nil to delete a key) + */ +static int seds_rangemap_set_value(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_rangemap"); + lua_settop(lua, 3); + lua_getuservalue(lua, 1); /* index 4 */ + + /* need to first check if the key already exists */ + /* check for an exact match */ + lua_pushvalue(lua, 2); + lua_rawget(lua, 4); + if (lua_isnil(lua, -1)) + { + /* need to try range checking to see if this key already exists */ + if (seds_rangemap_find_key(lua, 4, 2)) + { + /* duplicate key - should this throw an exception? */ + /* replace old value with new value, rather than adding a new key */ + lua_pop(lua, 1); + lua_replace(lua, 2); + } + } + + /* set the key in the table */ + lua_pushvalue(lua, 2); + lua_pushvalue(lua, 3); + lua_rawset(lua, 4); + + return 0; +} + +/** + * Lua callable meta function: Convert rangemap to a string + * + * Expected Stack args: + * 1: rangemap object + */ +static int seds_rangemap_tostring(lua_State *lua) +{ + luaL_Buffer buf; + bool is_empty; + + is_empty = true; + luaL_buffinit(lua, &buf); + luaL_addstring(&buf, "seds_rangemap:"); + lua_getuservalue(lua, 1); + lua_pushnil(lua); + while (lua_next(lua, -2)) + { + if (is_empty) + { + is_empty = false; + } + else + { + luaL_addstring(&buf, "+"); + } + luaL_addstring(&buf, luaL_tolstring(lua, -2, NULL)); + lua_pop(lua, 1); + luaL_addstring(&buf, " => "); + luaL_addstring(&buf, luaL_tolstring(lua, -1, NULL)); + lua_pop(lua, 2); + } + luaL_pushresult(&buf); + return 1; +} + +/** + * Lua callable meta function: iterate over the map + * + * Expected Stack args: + * 1: rangemap object + */ +static int seds_rangemap_get_pairs(lua_State *lua) +{ + luaL_checkudata(lua, 1, "seds_rangemap"); + lua_settop(lua, 1); + lua_getglobal(lua, "pairs"); + lua_getuservalue(lua, 1); + lua_call(lua, 1, 3); + return 3; +} + +/** + * Helper function: Pushes a userdata object of seds_rangemap + * The new map will be empty + */ +static int seds_rangemap_new(lua_State *lua) +{ + seds_range_ud_t *pmap = lua_newuserdata(lua, sizeof(seds_range_ud_t)); + + memset(pmap, 0, sizeof(*pmap)); + if (luaL_newmetatable(lua, "seds_rangemap")) + { + lua_pushstring(lua, "__index"); + lua_pushcfunction(lua, seds_rangemap_get_value); + lua_rawset(lua, -3); + lua_pushstring(lua, "__newindex"); + lua_pushcfunction(lua, seds_rangemap_set_value); + lua_rawset(lua, -3); + lua_pushstring(lua, "__tostring"); + lua_pushcfunction(lua, seds_rangemap_tostring); + lua_rawset(lua, -3); + lua_pushstring(lua, "__pairs"); + lua_pushcfunction(lua, seds_rangemap_get_pairs); + lua_rawset(lua, -3); + } + lua_setmetatable(lua, -2); + + /* start with an empty table */ + lua_newtable(lua); + lua_setuservalue(lua, -2); + + return 1; +} + +/*******************************************************************************/ +/* Externally-Called Functions */ +/* (referenced outside this unit and prototyped in a separate header) */ +/*******************************************************************************/ + +/* + * ------------------------------------------------------ + * External API function - see full details in prototype. + * ------------------------------------------------------ + */ +void seds_range_register_globals(lua_State *lua) +{ + luaL_checktype(lua, -1, LUA_TTABLE); + lua_pushcfunction(lua, seds_rangesegment_new); + lua_setfield(lua, -2, "new_rangesegment"); + lua_pushboolean(lua, true); + lua_pushcclosure(lua, seds_range_new, 1); + lua_setfield(lua, -2, "new_range_infinite"); + lua_pushboolean(lua, false); + lua_pushcclosure(lua, seds_range_new, 1); + lua_setfield(lua, -2, "new_range_empty"); + lua_pushcfunction(lua, seds_rangemap_new); + lua_setfield(lua, -2, "new_rangemap"); +} diff --git a/tool/src/seds_range.h b/tool/src/seds_range.h new file mode 100644 index 0000000..264da7d --- /dev/null +++ b/tool/src/seds_range.h @@ -0,0 +1,101 @@ +/* + * LEW-19710-1, CCSDS SOIS Electronic Data Sheet Implementation + * + * Copyright (c) 2020 United States Government as represented by + * the Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file seds_range.h + * \ingroup tool + * \author joseph.p.hickey@nasa.gov + * + * For full module description, see seds_range.c + */ + +#ifndef SEDS_RANGE_H +#define SEDS_RANGE_H + +#include "seds_global.h" + +/* Note with respect to the values: + * Not relying on IEEE-754 special values like NaN and infinity here, because + * it is not guaranteed/required that the platform supports these, and things + * get unclear with respect to comparisons when they are possibly involved. + * + * The value in this structure should always be a normal number, but the + * number is ignored if "is_defined" flag is false. + */ +typedef struct +{ + bool is_defined; /**< If false then anything matches (infinite) */ + bool is_inclusive; /**< Whether value itself it is included in the range */ + seds_number_t valx; /**< Value */ +} seds_rangelimit_t; + +/** + * Structure that tracks the range of EDS-defined objects + * + * Instances of these objects (in the form of Lua userdata objects) may be + * attached to any EDS DOM node which has a range associated with it. + */ +typedef struct +{ + seds_rangelimit_t min; + seds_rangelimit_t max; +} seds_rangesegment_t; + +typedef enum +{ + SEDS_RANGECONTENT_EMPTY, + SEDS_RANGECONTENT_NORMAL, + SEDS_RANGECONTENT_INFINITE +} seds_rangecontent_t; + +/** + * Structure that has no real members of its own + * + * The real data is a Lua value stored as the "uservalue" + * within this userdata object, along with a metatable for + * standard access functions which can be implemented in C. + * + * Instances of these objects (in the form of Lua userdata objects) may be + * attached to any EDS DOM node which has a range associated with it. + * + * seds_range: + * This combines one or more segments to allow for a disjointed range. + * + * seds_rangemap: + * Basically a Lua table that may use seds_range objects as keys. The + * lookup function knows how to compare ranges. + */ +typedef struct +{ + seds_rangecontent_t content; +} seds_range_ud_t; + +/*******************************************************************************/ +/* Function documentation and prototypes */ +/* (everything referenced outside this unit should be described here) */ +/*******************************************************************************/ + +/** + * Registers the range functions into the SEDS library object + * These are added to a table which is at the top of the stack + */ +void seds_range_register_globals(lua_State *lua); + +#endif /* SEDS_RANGE_H */ diff --git a/tool/src/seds_runtime.lua b/tool/src/seds_runtime.lua index 69682f9..447cf25 100644 --- a/tool/src/seds_runtime.lua +++ b/tool/src/seds_runtime.lua @@ -65,16 +65,22 @@ end -- This creates a filename of the form: -- qualifier_eds_filename -- --- where everything is converted to lowercase. If qualifier is nil, then the MISSION_NAME +-- where everything is converted to lowercase. If qualifier is nil, then the EDSTOOL_PROJECT_NAME -- global definition is used, and if that is also nil then it is left out entirely. -- SEDS.to_filename = function(filename, qualifier) if (not qualifier) then - qualifier = SEDS.get_define("MISSION_NAME") or "" + qualifier = SEDS.get_define("EDSTOOL_PROJECT_NAME") end - local result = string.lower(qualifier .. "_eds_" .. filename) + if (qualifier) then + qualifier = qualifier .. "_" + else + qualifier = "" + end + + local result = string.lower(qualifier .. "eds_" .. filename) -- In order to ensure that it is a safe filename on most typical filesystems, -- remove any non alphanumeric character and replace it with an underbar. @@ -136,16 +142,6 @@ SEDS.to_macro_name = function(ident) return result end -local CTYPEDEF_JPHFIX_TABLE = { - CONTAINER_DATATYPE = "Struct", -FLOAT_DATATYPE = "Atom", -INTEGER_DATATYPE = "Atom", -BOOLEAN_DATATYPE = "Atom", -ENUMERATION_DATATYPE = "Enum", -STRING_DATATYPE = "String", -ARRAY_DATATYPE = "Array" -} - local CTYPEDEF_QUALIFIER_TABLE = { GENERIC_TYPE = "Generic_", PARAMETER = "Parameter_", @@ -181,6 +177,11 @@ SEDS.to_ctype_typedef = function(ref,containment_style) -- if containment_style was set, then the qualifier should already be embedded within refstr if (containment_style) then prefix = "" + elseif (ref.implicit_basetype) then + -- There can actually be more than one type on a single entry, so this needs to include + -- the data type as well as the name to differentiate it and avoid collision. + prefix = "Implicit_" .. SEDS.to_safe_identifier(ref.entity_type) .. "_" + prefix = prefix:gsub("_DATATYPE_", "_") -- DATATYPE is redundant else prefix = "DataType_" end @@ -347,6 +348,11 @@ SEDS.any_datatype_filter = SEDS.create_nodetype_filter "GENERIC_TYPE" } +SEDS.generictype_filter = SEDS.create_nodetype_filter +{ + "GENERIC_TYPE" +} + -- A filter function that will match only containers (for base types) SEDS.container_filter = SEDS.create_nodetype_filter { @@ -421,9 +427,8 @@ SEDS.container_entry_filter = SEDS.create_nodetype_filter "CONTAINER_ERROR_CONTROL_ENTRY" } -SEDS.intf_entry_filter = SEDS.create_nodetype_filter +SEDS.command_filter = SEDS.create_nodetype_filter { - "PARAMETER", "COMMAND" } @@ -449,5 +454,18 @@ SEDS.constraint_filter = SEDS.create_nodetype_filter "RANGE_CONSTRAINT" } +SEDS.presence_constraint_filter = SEDS.create_nodetype_filter +{ + "PRESENCE_VALUE_CONSTRAINT", + "PRESENCE_TYPE_CONSTRAINT", + "PRESENCE_RANGE_CONSTRAINT" +} + +SEDS.namespace_filter = SEDS.create_nodetype_filter +{ + "PACKAGE", + "NAMESPACE" +} + -- End of file. Return the table object to the caller return SEDS diff --git a/tool/src/seds_tool_main.c b/tool/src/seds_tool_main.c index ae30a4a..22734dc 100644 --- a/tool/src/seds_tool_main.c +++ b/tool/src/seds_tool_main.c @@ -37,6 +37,7 @@ #include "seds_tree_node.h" #include "seds_instance_node.h" #include "seds_memreq.h" +#include "seds_range.h" #include "seds_outputfile.h" #include "seds_xmlparser.h" #include "seds_plugin.h" @@ -74,14 +75,16 @@ seds_toplevel_t sedstool; static const char *SEDS_REQUIRED_GLOBALS[] = { - "MISSION_BINARY_DIR", - "MISSION_NAME", + "EDSTOOL_OUTPUT_DIR", + "EDSTOOL_PROJECT_NAME", "CC", "LD", "AR", "CFLAGS", "LDFLAGS", - + "SRCDIR", + "INCDIR", + "OBJDIR" }; static void seds_export_environment(lua_State *lua) @@ -508,6 +511,7 @@ int main(int argc, char *argv[]) seds_tree_node_register_globals(lua); seds_instance_node_register_globals(lua); seds_memreq_register_globals(lua); + seds_range_register_globals(lua); seds_preprocess_register_globals(lua); seds_outputfile_register_globals(lua); seds_plugin_register_globals(lua); diff --git a/tool/src/seds_tree_methods.lua b/tool/src/seds_tree_methods.lua index 35a999b..a748a84 100644 --- a/tool/src/seds_tree_methods.lua +++ b/tool/src/seds_tree_methods.lua @@ -197,8 +197,38 @@ end -- local function get_flattened_name(node,suffix) local output = {} + local ref_entity = node + + -- Name qualifier table for use with seds_tree_node objects + local SYMBOLNAME_QUALIFIER_TABLE = { + ["DEFINE"] = "EdsParam", + ["DATASHEET"] = "EdsDatasheet", + ["NAMESPACE"] = "EdsNamespace", + ["PACKAGE"] = "EdsPackage", + ["PACKAGEFILE"] = "EdsPackage", + ["BOOLEAN_DATATYPE"] = "EdsBoolean", + ["INTEGER_DATATYPE"] = "EdsInteger", + ["CONTAINER_DATATYPE"] = "EdsContainer", + ["ARRAY_DATATYPE"] = "EdsArray", + ["FLOAT_DATATYPE"] = "EdsFloat", + ["ENUMERATION_DATATYPE"] = "EdsEnum", + ["ENUMERATION_ENTRY"] = "EdsLabel", + ["STRING_DATATYPE"] = "EdsString", + ["GENERIC_TYPE"] = "EdsGeneric", + ["COMPONENT"] = "EdsComponent", + ["DECLARED_INTERFACE"] = "EdsInterface", + ["PROVIDED_INTERFACE"] = "EdsInterface", + ["REQUIRED_INTERFACE"] = "EdsInterface", + ["COMMAND"] = "EdsCommand" + } + + while (ref_entity.entity_type == "ALIAS_DATATYPE") do + ref_entity = ref_entity.type + end + -- Note that LUA treats "nil" table values as unassigned -- So if any of the inputs are nil it will simply be omitted + output[1 + #output] = SYMBOLNAME_QUALIFIER_TABLE[ref_entity.entity_type] output[1 + #output] = get_qualified_name(node) output[1 + #output] = suffix @@ -212,29 +242,11 @@ end -- Name qualifier table for use with seds_tree_node objects local CTYPE_QUALIFIER_TABLE = { ["packed"] = "PackedBuffer_", + ["align"] = "AlignedBuffer_", ["native"] = "NativeBuffer_", [true] = "NativeBuffer_" } -local CTYPE_JPHFIX_TABLE = { - CONTAINER_DATATYPE = "Struct", - FLOAT_DATATYPE = "Atom", - INTEGER_DATATYPE = "Atom", - BOOLEAN_DATATYPE = "Atom", - GENERIC_TYPE = "Generic", - ENUMERATION_DATATYPE = "Enum", - STRING_DATATYPE = "String", - ARRAY_DATATYPE = "Array", - PARAMETER = "Parameter", - COMMAND = "Command", - ARGUMENT = "Argument", - COMPONENT = "Component", - VARIABLE = "Variable", - DECLARED_INTERFACE = "Interface", - PROVIDED_INTERFACE = "Interface", - REQUIRED_INTERFACE = "Interface" -} - -- ------------------------------------------------- -- Helper function to write an integer typedef -- ------------------------------------------------- @@ -250,8 +262,13 @@ local function get_ctype_basename(node, containment_style) prefix = containment_style .. "_" end + -- For implicitly created types, add a unique prefix so that there + -- is no chance of a name collision with a non-implicit type + if (prefix and node.implicit_basetype) then + prefix = "Implicit" .. prefix + end + return (prefix or "") .. SEDS.to_safe_identifier(node:get_qualified_name()) - --return SEDS.to_safe_identifier(node:get_qualified_name()) end -- ------------------------------------------------------------------------- diff --git a/tool/src/seds_tree_node.c b/tool/src/seds_tree_node.c index 9406e34..93e2c5d 100644 --- a/tool/src/seds_tree_node.c +++ b/tool/src/seds_tree_node.c @@ -135,6 +135,9 @@ static const char *SEDS_NODETYPE_LOOKUP[SEDS_NODETYPE_MAX] = [SEDS_NODETYPE_TYPE_CONSTRAINT] = "TYPE_CONSTRAINT", [SEDS_NODETYPE_RANGE_CONSTRAINT] = "RANGE_CONSTRAINT", [SEDS_NODETYPE_VALUE_CONSTRAINT] = "VALUE_CONSTRAINT", + [SEDS_NODETYPE_PRESENCE_TYPE_CONSTRAINT] = "PRESENCE_TYPE_CONSTRAINT", + [SEDS_NODETYPE_PRESENCE_RANGE_CONSTRAINT] = "PRESENCE_RANGE_CONSTRAINT", + [SEDS_NODETYPE_PRESENCE_VALUE_CONSTRAINT] = "PRESENCE_VALUE_CONSTRAINT", [SEDS_NODETYPE_CONSTRAINT_LAST] = "CONSTRAINT_LAST", [SEDS_NODETYPE_ENCODING_FIRST] = "ENCODING_FIRST", [SEDS_NODETYPE_INTEGER_DATA_ENCODING] = "INTEGER_DATA_ENCODING", @@ -362,12 +365,17 @@ static int seds_tree_node_get_property(lua_State *lua) */ static int seds_tree_node_to_string(lua_State *lua) { - seds_node_t *pnode = lua_touserdata(lua, 1); + seds_node_t *pnode = luaL_checkudata(lua, 1, "seds_node"); + int uv_idx; luaL_Buffer buf; + /* Note - As of Lua 5.4, this may push something onto the stack. + * Whereas in 5.3 and below, it does not change the stack. */ luaL_buffinit(lua, &buf); - lua_getuservalue(lua, 1); /* stack pos = 2 */ + lua_getuservalue(lua, 1); + uv_idx = lua_gettop(lua); /* abs position of the uv */ + if (pnode->node_type < SEDS_NODETYPE_MAX) { luaL_addstring(&buf, SEDS_NODETYPE_LOOKUP[pnode->node_type]); @@ -376,13 +384,13 @@ static int seds_tree_node_to_string(lua_State *lua) { luaL_addstring(&buf, SEDS_NODETYPE_LOOKUP[SEDS_NODETYPE_UNKNOWN]); } - lua_getfield(lua, 2, "name"); + lua_getfield(lua, uv_idx, "name"); if (lua_isstring(lua, -1)) { luaL_addstring(&buf, " name="); luaL_addstring(&buf, lua_tostring(lua, -1)); } - lua_settop(lua, 1); + lua_settop(lua, uv_idx - 1); luaL_pushresult(&buf); return 1; } @@ -503,4 +511,3 @@ void seds_tree_node_register_globals(lua_State *lua) lua_rawsetp(lua, LUA_REGISTRYINDEX, &SEDS_TREE_PROPERTIES_LUA_KEY); } - diff --git a/tool/src/seds_tree_node.h b/tool/src/seds_tree_node.h index 5dec757..3e84112 100644 --- a/tool/src/seds_tree_node.h +++ b/tool/src/seds_tree_node.h @@ -147,6 +147,9 @@ typedef enum SEDS_NODETYPE_TYPE_CONSTRAINT, SEDS_NODETYPE_RANGE_CONSTRAINT, SEDS_NODETYPE_VALUE_CONSTRAINT, + SEDS_NODETYPE_PRESENCE_TYPE_CONSTRAINT, + SEDS_NODETYPE_PRESENCE_RANGE_CONSTRAINT, + SEDS_NODETYPE_PRESENCE_VALUE_CONSTRAINT, SEDS_NODETYPE_CONSTRAINT_LAST, /**< Reserved index marker - not used */ /* Encoding specifiers */ diff --git a/tool/src/seds_xmlparser.c b/tool/src/seds_xmlparser.c index d5d0bd8..40e0927 100644 --- a/tool/src/seds_xmlparser.c +++ b/tool/src/seds_xmlparser.c @@ -235,6 +235,11 @@ static const seds_stringmap_t XML_SEDS_STARTTAG_MAP[] = { .tag_name = XML_CHAR_C("ProvidedInterface"), .tag_id = SEDS_NODETYPE_PROVIDED_INTERFACE }, { .tag_name = XML_CHAR_C("RequiredInterface"), .tag_id = SEDS_NODETYPE_REQUIRED_INTERFACE }, + /* these are variations of the constraint elements that appear within PresentWhen elements */ + { .tag_name = XML_CHAR_C("PresenceTypeConstraint"), .tag_id = SEDS_NODETYPE_PRESENCE_TYPE_CONSTRAINT }, + { .tag_name = XML_CHAR_C("PresenceRangeConstraint"), .tag_id = SEDS_NODETYPE_PRESENCE_RANGE_CONSTRAINT }, + { .tag_name = XML_CHAR_C("PresenceValueConstraint"), .tag_id = SEDS_NODETYPE_PRESENCE_VALUE_CONSTRAINT }, + /* Keep NULL tag last - absolute end of list marker */ { .tag_name = NULL, .tag_id = SEDS_NODETYPE_UNKNOWN } }; @@ -334,6 +339,29 @@ static seds_nodetype_t seds_xmlparser_identify_element(seds_nodetype_t parent_no } } + /* + * The same constraint XML elements are also used within PresentWhen, but these + * need to be handled differently from other constraint elements because they are + * used for a different purpose in this context + */ + if (parent_node_type == SEDS_NODETYPE_PRESENT_WHEN) + { + switch(node_type) + { + case SEDS_NODETYPE_VALUE_CONSTRAINT: + node_type = SEDS_NODETYPE_PRESENCE_VALUE_CONSTRAINT; + break; + case SEDS_NODETYPE_TYPE_CONSTRAINT: + node_type = SEDS_NODETYPE_PRESENCE_TYPE_CONSTRAINT; + break; + case SEDS_NODETYPE_RANGE_CONSTRAINT: + node_type = SEDS_NODETYPE_PRESENCE_RANGE_CONSTRAINT; + break; + default: + break; + } + } + return node_type; } diff --git a/validation/eds/ccsds_sois_sampletypes.xml b/validation/eds/ccsds_sois_sampletypes.xml new file mode 100644 index 0000000..7813a3d --- /dev/null +++ b/validation/eds/ccsds_sois_sampletypes.xml @@ -0,0 +1,720 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + single + + + + + + + single + + + + + + + double + + + + + + + double + + + + + + + quad + + + + + + + double + + + + + + + single + + + + + + + quad + + + + + + + double + + + + + + + single + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/validation/eds/ccsds_sois_seds.xml b/validation/eds/ccsds_sois_seds.xml new file mode 100644 index 0000000..cd7cc5e --- /dev/null +++ b/validation/eds/ccsds_sois_seds.xml @@ -0,0 +1,175 @@ + + + + This namespace contains some generally-useful types, including:<ul><li> standard c-like types with defined encoding and range<li> timestamps, quaternians, spin rates and similar space-domain quantities</ul> + + + + + + + + single + + + + + + double + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In mathematics, the quaternions are a number system that extends the complex numbers. They were first described by Irish mathematician William Rowan Hamilton in 1843 and applied to mechanics in three-dimensional space. A feature of quaternions is that the product of two quaternions is noncommutative. Hamilton defined a quaternion as the quotient of two directed lines in a three-dimensional space or equivalently as the quotient of two vectors. Quaternions can also be represented as the sum of a scalar and a vector. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The traditional orbital elements are the six Keplerian elements, after Johannes Kepler and his laws of planetary motion.When viewed from an inertial frame, two orbiting bodies trace out distinct trajectories. Each of these trajectories has its focus at the common center of mass. When viewed from the non-inertial frame of one body only the trajectory of the opposite body is apparent; Keplerian elements describe these non-inertial trajectories. An orbit has two sets of Keplerian elements depending on which body is used as the point of reference. The reference body is called the primary, the other body is called the secondary. In the case of spacecraft orbiting an astronomical body, the spacecraft is always considered the secondary. + + + The sum of the periapsis and apoapsis distances divided by two. + + + The shape of the ellipse, describing how much it is elongated compared to a circle. + + + + + + Vertical tilt of the ellipse with respect to the reference plane, measured at the ascending node. + + + + + + Angle from a reference direction to the direction of the ascending node, measured in a reference frame. + + + Defines the orientation of the ellipse in the orbital plane, as an angle measured from the ascending node to the periapsis. + + + Defines the position of the orbiting body along the ellipse at a specific time (the "epoch"). The geometric angle in the plane of the ellipse, between periapsis (closest approach to the central body) and the position of the orbiting object at any given time. + + + + + Lossless representation for all defined CCSDS encodings up to 4 octets of coarse time.This allows a time code representation of time with an accuracy of 60ns through the year 2094. The epoch is the TAI epoch of 1958 January 1.This time code is <b>not</b> UTC-based and leap second corrections do not apply. + + + + + + + + + + + Lossless representation for all defined CCSDS encodings up to 4 octets of day number time.The epoch is the TAI epoch of 1958 January 1. + + + + + + + + + + + + diff --git a/edslib/unit-test/eds/UT1.xml b/validation/eds/utapp.xml similarity index 61% rename from edslib/unit-test/eds/UT1.xml rename to validation/eds/utapp.xml index 6e92642..e1aa675 100644 --- a/edslib/unit-test/eds/UT1.xml +++ b/validation/eds/utapp.xml @@ -1,5 +1,5 @@ - + @@ -39,31 +39,31 @@ - + - + - + - + - + @@ -72,14 +72,14 @@ - - - - - - - - + + + + + + + + @@ -98,10 +98,10 @@ - - - - + + + + @@ -112,39 +112,64 @@ - + - + - + - - - - + + + + - + - + - - - - + + + + + - + - + + + + + + + + + + + + + + + + + + + + - diff --git a/validation/eds/uthdr.xml b/validation/eds/uthdr.xml new file mode 100644 index 0000000..208aa68 --- /dev/null +++ b/validation/eds/uthdr.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2ee125dd836fd1f18da83e5dba88a668927b3a0d Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 23 Jan 2026 11:04:25 -0500 Subject: [PATCH 2/2] Update dependencies in validation test workflow --- .github/workflows/validation-test.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validation-test.yml b/.github/workflows/validation-test.yml index 0a481ba..f900172 100644 --- a/.github/workflows/validation-test.yml +++ b/.github/workflows/validation-test.yml @@ -69,6 +69,9 @@ jobs: steps: + - name: Install Build Dependencies + run: sudo apt-get install liblua5.4-dev libpython3-dev libjson-c-dev pkg-config lcov xsltproc -y + # Note - caches were updated in "prepare-dependencies" job so all three of these should be a hit - name: Retrieve Cached EdsLib Source uses: actions/cache@v3 @@ -88,10 +91,10 @@ jobs: # so in order to make this work it needs to adjust this. - name: Import OSAL run: | + sudo find ${GITHUB_WORKSPACE}/osal-staging -type d -print0 | xargs -0 chmod go+rx + sudo find ${GITHUB_WORKSPACE}/osal-staging -type f -print0 | xargs -0 chmod go+r + sudo find ${GITHUB_WORKSPACE}/osal-staging -type f -path '*/bin/*' -print0 | xargs -0 chmod go+x sudo cp -rv -t / ${GITHUB_WORKSPACE}/osal-staging/* - sudo find /usr/local -type d -exec chmod go+rx {} \; - sudo find /usr/local -type f -exec chmod go+r {} \; - sudo find /usr/local -type f -path '*/bin/*' -exec chmod go+x {} \; - name: Refresh Dynamic Linker Cache run: sudo ldconfig