Skip to content

JSON encoder: align keys to camelCase and support flat/nested formats#6

Merged
Thecave3 merged 10 commits into
mainfrom
json-format
Jun 12, 2026
Merged

JSON encoder: align keys to camelCase and support flat/nested formats#6
Thecave3 merged 10 commits into
mainfrom
json-format

Conversation

@freebacca

Copy link
Copy Markdown
Contributor
  • Align all JSON key names to camelCase to match the ASN.1 naming convention (e.g., dAppName, responseCode, ranFunctionList)
  • Align PDU type values to camelCase (e.g., setupRequest instead of SetupRequest)
  • Support both flat and nested JSON message formats in the decoder: if the incoming message has a data wrapper, payload is read from it; otherwise, payload is read from root
  • Encoder mirrors the format of the last decoded message (flat or nested), preserving backward compatibility
  • Existing tests updated and passing (14/14)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the JSON encoder/decoder to use camelCase field names and camelCase PDU type strings, and introduces support for decoding both “flat” JSON messages (payload at root) and “nested” messages (payload under a data wrapper), with the encoder attempting to mirror the last decoded format.

Changes:

  • Renames JSON envelope keys and payload keys to camelCase (e.g., type, id, dAppName, ranFunctionList) and updates PDU type string values to camelCase.
  • Updates decoding to accept both flat and nested (data-wrapped) formats and makes encoding optionally emit nested payloads based on the last decode.
  • Adjusts CMake find_package minimum version for nlohmann_json and updates JSON encoder tests for new PDU type strings.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
tests/test_json_encoder.cpp Updates assertions to match new camelCase PDU type strings in encoded JSON.
src/encoder/json_encoder.hpp Adds nested_mode_ state used to choose flat vs nested encoding format.
src/encoder/json_encoder.cpp Implements camelCase keys, new PDU type string mapping, and flat/nested decode + stateful encode mirroring.
cmake/libe3Dependencies.cmake Lowers the minimum nlohmann_json version requested via find_package.
Comments suppressed due to low confidence (1)

cmake/libe3Dependencies.cmake:21

  • CMake now accepts an installed nlohmann_json as old as 3.10, but the FetchContent fallback still pins v3.11.3. This can lead to different behavior/ABI depending on environment.

Consider aligning the minimum find_package version with the fetched tag (or update the fetched tag to a 3.10.x release if 3.10 support is the goal).

    find_package(nlohmann_json 3.10 QUIET)
    if(NOT nlohmann_json_FOUND)
        include(FetchContent)
        FetchContent_Declare(
            nlohmann_json
            GIT_REPOSITORY https://github.com/nlohmann/json.git
            GIT_TAG v3.11.3
            GIT_SHALLOW TRUE

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/encoder/json_encoder.cpp
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
@Thecave3 Thecave3 closed this Mar 24, 2026
@mychele mychele reopened this Mar 27, 2026
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.cpp Outdated
Comment thread src/encoder/json_encoder.hpp Outdated
Comment thread tests/test_json_encoder.cpp
Comment thread src/encoder/json_encoder.cpp

Copilot AI commented Apr 14, 2026

Copy link
Copy Markdown

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Copilot AI requested a review from Thecave3 April 14, 2026 17:34
@Thecave3 Thecave3 removed their request for review April 14, 2026 17:37
@Thecave3 Thecave3 force-pushed the json-format branch 2 times, most recently from b73fb51 to 4ec3cb0 Compare May 15, 2026 20:42
@github-actions

github-actions Bot commented May 15, 2026

Copy link
Copy Markdown
Contributor

📊 MPMC Queue Benchmark Results (commit af826b4)

MpmcQueue End-to-End Latency (SPSC, 30000 items per queue size)

Queue Capacity P50 (ns) P95 (ns) P99 (ns) P99.9 (ns)
16 647 687 708 24000
64 2441 2561 2671 27207
256 6889 9945 30634 34420
1024 13151 13381 32466 32517
4096 18418 18830 36612 36633

ResponseQueue End-to-End Latency (SPSC, 30000 items per queue size)

Queue Capacity P50 (ns) P95 (ns) P99 (ns)
16 1078 1810 92680
64 15820 22593 37831
256 38963 60162 84528
1024 130515 153958 182912
4096 514372 585956 586518

Throughput (400000 items per configuration)

Configuration Queue Cap Throughput (Mops/s)
SPSC (1P×1C) 256 33.81
MPSC (4P×1C) 1024 40.58
MPMC (4P×4C) 4096 54.81
MPMC (8P×4C) 4096 26.86

Stress / Correctness (100000 items each)

Test Items Result
SPSC (1P×1C) 100000 ✅ PASS
MPSC (4P×1C) 100000 ✅ PASS
MPMC (4P×4C) 100000 ✅ PASS
MPMC (8P×8C) 100000 ✅ PASS

Benchmarked on ubuntu-latest, Release build

@Thecave3

Thecave3 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Once this is ready to be merged i will review it again. one minor note: if possible let's keep json at the 3.11 version. Feel free to set this as ready once we can review it

@Thecave3 Thecave3 marked this pull request as draft June 4, 2026 22:51
@freebacca

freebacca commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

OSRB has been approved. I have added dvilla in CONTRIBUTORS, and reverted the change of JSON to 3.11. The MR is now ready to be reviewed and merged.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

⏱️ Full-loop Latency Benchmark (commit ac596bb)

Full-loop latency benchmark (N=1060 after 50 warmup)

All values in microseconds (us). Transport: ZMQ over IPC, encoding: ASN.1 APER.

Phase mean p50 p99 max
1. Collect indication data 0 0 0 7
2. Create & encode indication 0 0 0 8
3. Deliver indication (RAN -> dApp) 83 78 150 518
4. Decode indication 0 0 0 8
5. Process data 0 0 0 0
6. Create & encode control 0 0 0 1
7. Deliver control (dApp -> RAN) 101 103 158 170
8. Decode & handle control 0 0 0 6
Total round-trip 187 174 299 634

Benchmarked on ubuntu-latest, Release build, ZMQ + IPC, ASN.1 APER.

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🔄 E2E dApp Integration Results (commit ac596bb)

posix/ipc

  • dApp exit: 0
  • Indications received: 7

posix/tcp

  • dApp exit: 0
  • Indications received: 7

zmq/ipc

  • dApp exit: 0
  • Indications received: 7

zmq/tcp

  • dApp exit: 0
  • Indications received: 7

example_simple_agent + example_simple_dapp on ubuntu-latest, Release build

@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

🔀 E2E Topologies — multi-dApp / multi-RAN (commit ac596bb)

zmq/ipc

  • 1 RAN - 1 dApp: indications=5
    • dapp peer=t11 ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.4 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=t12 ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.6 max=1 @seq=2) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=t12 ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=0.5 max=1 @seq=2) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=t2a ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.4 max=1 @seq=2) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=t2b ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.4 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]

zmq/tcp

  • 1 RAN - 1 dApp: indications=5
    • dapp peer=default ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.2 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=default ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.6 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=default ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=0.5 max=1 @seq=0) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=default ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0.8 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=off100 ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]

example_simple_agent + example_simple_dapp on ubuntu-latest, Release. Indication age is report-only.

@freebacca freebacca marked this pull request as ready for review June 10, 2026 19:24
Comment thread src/core/e3_interface.cpp
Comment thread include/libe3/subscription_manager.hpp
Comment thread include/libe3/subscription_manager.hpp
Comment thread src/core/e3_agent.cpp
Comment thread src/core/e3_agent.cpp
Comment thread src/encoder/json_encoder.cpp
Comment thread CONTRIBUTORS Outdated

@Thecave3 Thecave3 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments, if possible can you run and paste the test for the json one or can you update the template include the json in the tests done by the CI?

This would ensure that changes are preserved over the updates

@Thecave3

Thecave3 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Tests for json included in #27 , please update this branch to the latest version once #27 is merged.

@Thecave3

Thecave3 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Encoder threads are all resolved and CI is green. Two items left before merge:

Blocking

  • Control IDs are dropped end-to-end (subscription_manager.hpp). SubscriptionRequest.control_identifier_list already exists on the wire (since First iteration for the library #4) but is never passed at the add_subscription() call site in e3_interface.cpp, so a dApp's requested control ids are silently discarded. Now that we honor per-dApp telemetry + periodicity, control should be symmetric: add control_ids to SubscriptionDetails, thread it through add_subscription() and the call site, and add a matching e3_agent_get_subscription_control_ids C-API getter.

Cleanup

  • Redundant fields in SubscriptionDetails(dapp_id, ran_function_id) is already the composite map key (make_sub_key), so storing them again inside the value duplicates the key and can drift. Drop them; keep only the per-subscription metadata.

Resolved by author's answers (no code change)

  • e3_interface.cpp subscription-delete negative case: confirmed — remove_subscription_by_id fails when the id is absent or belongs to another dApp.
  • e3_agent.cpp null get_subscription_details: ternary returns 0 = use SM default periodicity, safe.
  • e3_agent.cpp empty-vector fallback: required by the ternary's common type; an empty std::vector does no heap allocation.

@Thecave3

Copy link
Copy Markdown
Collaborator

Great work, thanks for this contribution. One final thing, please rebase and bump the version to 0.0.6. Afterwards, I will approve the PR and include it with squash and merge.

freebacca and others added 9 commits June 12, 2026 21:27
…ct unknown PDU types

- Derive camelCase PDU type strings from pdu_type_to_string() instead of separate pdu_type_to_json_string() switch
- Remove backward-compat fallbacks for legacy key names (pdu_type, message_id)
- Return std::optional from string_to_pdu_type; fail decode on unrecognized types
- Add Doxygen documentation on JsonE3Encoder class and nested_mode_ member
- Add tests: nested format decode, encode mirrors nested format, PascalCase rejection
Drop nested_mode_ member. Encoder always merges payload fields at root.
Decoder rejects messages containing a "data" wrapper with DECODE_FAILED.
Update indication-message tests to use valid JSON in protocol_data so
they exercise the json::parse() path the encoder now requires.
Add per-subscription control_ids alongside telemetry_ids, threaded through
add_subscription() and exposed via get_subscription_control_ids() and the
matching C API. SubscriptionRequest already carries control_identifier_list
on the wire, so no encoder change. Also drop dapp_id/ran_function_id from
SubscriptionDetails since they duplicate the composite map key and can drift.
@freebacca

Copy link
Copy Markdown
Contributor Author

Great work, thanks for this contribution. One final thing, please rebase and bump the version to 0.0.6. Afterwards, I will approve the PR and include it with squash and merge.

Done and tests run with no issues.

@Thecave3 Thecave3 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, thanks for this contribution.

@Thecave3 Thecave3 merged commit 1451e9a into main Jun 12, 2026
14 checks passed
@Thecave3 Thecave3 deleted the json-format branch June 12, 2026 22:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants