diff --git a/.devcontainer/scripts/bashrc_addition.sh b/.devcontainer/scripts/bashrc_addition.sh index 6cde26d..fdb1296 100644 --- a/.devcontainer/scripts/bashrc_addition.sh +++ b/.devcontainer/scripts/bashrc_addition.sh @@ -35,6 +35,7 @@ Workspace Folder ${SECONDARY_COLOR}$WORKSPACE_FOLDER ${INFO_COLOR}Library Version(s)${PRIMARY_COLOR} Rust Version ${SECONDARY_COLOR}$(rustc --version)${PRIMARY_COLOR} Cargo Version ${SECONDARY_COLOR}$(cargo --version)${PRIMARY_COLOR} +Java Version ${SECONDARY_COLOR}$(java --version)${PRIMARY_COLOR} ${INFO_COLOR}Git${PRIMARY_COLOR} Username ${SECONDARY_COLOR}$(git config user.name)${PRIMARY_COLOR} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..ab72e9d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,60 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in library 'up_transport_iceoryx2_rust'", + "cargo": { + "args": [ + "test", + "--no-run", + "--lib", + "--package=up-transport-iceoryx2-rust" + ], + "filter": { + "name": "up_transport_iceoryx2_rust", + "kind": "lib" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'subscriber'", + "cargo": { + "args": [ + "build", + "--example=subscriber", + "--package=up-transport-iceoryx2-rust" + ], + "filter": { + "name": "subscriber", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'publisher'", + "cargo": { + "args": [ + "build", + "--example=publisher", + "--package=up-transport-iceoryx2-rust" + ], + "filter": { + "name": "publisher", + "kind": "example" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2d7d142..a4675aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,16 +60,14 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools", - "lazy_static", - "lazycell", "log", "prettyplease", "proc-macro2", @@ -78,7 +76,6 @@ dependencies = [ "rustc-hash", "shlex", "syn", - "which", ] [[package]] @@ -101,10 +98,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.26" +version = "1.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" dependencies = [ + "find-msvc-tools", "shlex", ] @@ -144,12 +142,33 @@ dependencies = [ "libloading", ] +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.16", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "enum-iterator" version = "2.1.0" @@ -193,21 +212,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fnv" -version = "1.0.7" +name = "find-msvc-tools" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" [[package]] -name = "getrandom" -version = "0.2.16" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" @@ -229,9 +243,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" @@ -248,15 +262,27 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "hostname" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +dependencies = [ + "cfg-if", + "libc", + "windows-link", +] + [[package]] name = "iceoryx2" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a462c1baccde41be91001f7b36cb1d1afe313c2afdb5a6c16174ae7ca917b6ef" +checksum = "d2674b41c16478e52a05a4da49555205ccdd2f5fc153faee5bbf58484149a670" dependencies = [ "iceoryx2-bb-container", "iceoryx2-bb-derive-macros", "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-lock-free", "iceoryx2-bb-log", "iceoryx2-bb-memory", @@ -264,6 +290,7 @@ dependencies = [ "iceoryx2-bb-system-types", "iceoryx2-cal", "iceoryx2-pal-concurrency-sync", + "iceoryx2-pal-configuration", "serde", "tiny-fn", "toml", @@ -271,12 +298,13 @@ dependencies = [ [[package]] name = "iceoryx2-bb-container" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6205871fd23e67215ec4dff6b9895f7526325b498b2eb759b93399ee69d6d89" +checksum = "1436071c05750d19dc6dfe78dae6823cef0fe6d902fc5ed9b3626af0bdcb72a5" dependencies = [ "iceoryx2-bb-derive-macros", "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-log", "iceoryx2-pal-concurrency-sync", "serde", @@ -284,11 +312,12 @@ dependencies = [ [[package]] name = "iceoryx2-bb-derive-macros" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468faacface5d0252a090d565d9da86c05dc77e822abebdc0affeaad9021cce6" +checksum = "574ab1305d422a19889ae8382347a3e0281654fa694aacd8e6953b41e0ed95a9" dependencies = [ "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "proc-macro2", "quote", "syn", @@ -296,9 +325,9 @@ dependencies = [ [[package]] name = "iceoryx2-bb-elementary" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9af6479bb3aed7cfb398e54e91a769629d64f5bc2ede8c6a528ee02daa1675a5" +checksum = "df7def5420ba8eac9210c98ca2743fc08b568de5a5802dc83079ebc406d02c12" dependencies = [ "iceoryx2-bb-elementary-traits", "iceoryx2-pal-concurrency-sync", @@ -306,41 +335,42 @@ dependencies = [ [[package]] name = "iceoryx2-bb-elementary-traits" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb55d8c8b866531332904fd5a7041355d67d91d722f2e22850c1364ca779d30" +checksum = "b5baf086b219796879a97414094e3cc4c98a8024cb3e6471e2cf9d3d407b6f1d" dependencies = [ "iceoryx2-pal-concurrency-sync", ] [[package]] name = "iceoryx2-bb-lock-free" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9e850529efc04e4f70789fc8a8016883cda0ee9b88a078f0b6d296cc966b11" +checksum = "998e662cd56cd477f0ff3f7f5e3f28dbdf836579de036cfe0ec9edf465ec63d6" dependencies = [ "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-log", "iceoryx2-pal-concurrency-sync", ] [[package]] name = "iceoryx2-bb-log" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54d933cadfd6ac326dbce588fa5e1203b1b85234899a1742ad16a64490631dd" +checksum = "d98aca37ed1a534d6ecef046f57be2cc9435bf8f222bae86b83c9f01f3adf7be" dependencies = [ "iceoryx2-pal-concurrency-sync", - "termsize", ] [[package]] name = "iceoryx2-bb-memory" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b03e120e45d869979da01f894c226ed24c49feb3a318acbb7106021962d205" +checksum = "2468bcaa25831c80439403d9673f42689177d4a2d4ebe6cd63dc684a44afe973" dependencies = [ "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-lock-free", "iceoryx2-bb-log", "iceoryx2-bb-posix", @@ -349,14 +379,15 @@ dependencies = [ [[package]] name = "iceoryx2-bb-posix" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc53c019df6e0aa5df7bf8052cc11064a4ba19fe7bc34671f8ce13c2eb7ac3ab" +checksum = "2f33015988d54a7294e2a184066cb73cd52103d5b6b7ba6dcf1163553ba16763" dependencies = [ "enum-iterator", "iceoryx2-bb-container", "iceoryx2-bb-derive-macros", "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-log", "iceoryx2-bb-system-types", "iceoryx2-pal-concurrency-sync", @@ -369,13 +400,14 @@ dependencies = [ [[package]] name = "iceoryx2-bb-system-types" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346e9593393627c2cbc17b8663a0650c8e33377276f2e06e10346816705ae5cb" +checksum = "7ff38eb9362cc3268ad0225d805cf7fba2ac1800123d65b6e951dff8c3b6fd20" dependencies = [ "iceoryx2-bb-container", "iceoryx2-bb-derive-macros", "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-log", "iceoryx2-pal-configuration", "iceoryx2-pal-posix", @@ -384,14 +416,15 @@ dependencies = [ [[package]] name = "iceoryx2-cal" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9941d8beb989b80b581d2a6871e4b464b695bc1f86076117be1eb9c74f3dcb" +checksum = "1708090d319ebe74a941c1b0297154a2dfccdf69a139e51fcf930f44326f1be2" dependencies = [ "cdr", "iceoryx2-bb-container", "iceoryx2-bb-derive-macros", "iceoryx2-bb-elementary", + "iceoryx2-bb-elementary-traits", "iceoryx2-bb-lock-free", "iceoryx2-bb-log", "iceoryx2-bb-memory", @@ -399,6 +432,7 @@ dependencies = [ "iceoryx2-bb-system-types", "iceoryx2-pal-concurrency-sync", "once_cell", + "postcard", "serde", "sha1_smol", "tiny-fn", @@ -407,21 +441,21 @@ dependencies = [ [[package]] name = "iceoryx2-pal-concurrency-sync" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bc07c3be1bce2afef70dc5597abe14bdadde367f759243e92325a96733163d6" +checksum = "d2d54284a074dea1fd7070eae77b001833a576b39b519d6b0586341c4486a0c8" [[package]] name = "iceoryx2-pal-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a94e78e84f673da8ee4b46cb5df643792b046b7118678da9969fee29cf15c68" +checksum = "3bdb14bb2cda075b3190a2c41b1a0eff85842fe0ac8d1eaa10b68f9cde4a7fe7" [[package]] name = "iceoryx2-pal-posix" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5616e41d3171df75d1ab9577d5bccdc4fb95102e275db2afd84a9828a0a01918" +checksum = "fd2ff4c48a8b992baabb09a573db9446bff89e39a193500dd5c8f8196c241c63" dependencies = [ "bindgen", "cc", @@ -442,6 +476,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "itertools" version = "0.12.1" @@ -457,12 +502,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.172" @@ -476,7 +515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.52.6", ] [[package]] @@ -499,9 +538,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "mediatype" -version = "0.19.20" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" +checksum = "f490ea2ae935dd8ac89c472d4df28c7f6b87cc20767e1b21fd5ed6a16e7f61e4" [[package]] name = "memchr" @@ -524,6 +563,17 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + [[package]] name = "nom" version = "7.1.3" @@ -561,6 +611,18 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -572,9 +634,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" dependencies = [ "proc-macro2", "syn", @@ -598,7 +660,7 @@ dependencies = [ "bytes", "once_cell", "protobuf-support", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -613,7 +675,7 @@ dependencies = [ "protobuf-parse", "regex", "tempfile", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -628,7 +690,7 @@ dependencies = [ "protobuf", "protobuf-support", "tempfile", - "thiserror", + "thiserror 1.0.69", "which", ] @@ -638,7 +700,7 @@ version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -715,20 +777,19 @@ checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" -version = "0.8.5" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -736,11 +797,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.2.16", + "getrandom", ] [[package]] @@ -780,9 +841,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" @@ -851,6 +912,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "syn" version = "2.0.101" @@ -869,29 +945,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom", "once_cell", "rustix 1.0.7", "windows-sys 0.59.0", ] [[package]] -name = "termsize" -version = "0.1.9" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f11ff5c25c172608d5b85e2fb43ee9a6d683a7f4ab7f96ae07b3d8b590368fd" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "libc", - "winapi", + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.69" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.16", ] [[package]] @@ -905,20 +980,49 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiny-fn" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fde9a76dac5751480f711f327371c809d7f8a9f036436e6237d67859adbf3bd" +checksum = "9659b108631d1e1cf3e8e489f894bee40bc9d68fd6cc67ec4d4ce9b72d565228" [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", + "io-uring", + "libc", + "mio", "pin-project-lite", + "signal-hook-registry", + "slab", + "tokio-macros", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -990,9 +1094,9 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "up-rust" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616da735a2c488128e67d5ce16113f4303c83a5dbeb7c281ae893da281df2a57" +checksum = "8744bbbd9090e901587401bdceb9b6369da4fe2925480587aa8e89e166552270" dependencies = [ "async-trait", "bytes", @@ -1001,7 +1105,7 @@ dependencies = [ "protobuf-codegen", "protoc-bin-vendored", "rand", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", "uriparse", @@ -1013,7 +1117,11 @@ name = "up-transport-iceoryx2-rust" version = "0.1.0" dependencies = [ "async-trait", + "hostname", "iceoryx2", + "iceoryx2-bb-container", + "protobuf", + "tokio", "up-rust", ] @@ -1071,26 +1179,10 @@ dependencies = [ ] [[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-link" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-sys" @@ -1134,29 +1226,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -1169,12 +1245,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -1187,12 +1257,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -1205,24 +1269,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -1235,12 +1287,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -1253,12 +1299,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -1271,12 +1311,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -1289,17 +1323,11 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 9f7e8dd..d874047 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,13 @@ name = "up-transport-iceoryx2-rust" version = "0.1.0" edition = "2024" +keywords = ["uProtocol", "SDK", "communication", "iceoryx", "iceoryx2"] [dependencies] -up-rust = "0.5" -iceoryx2 = "0.6.1" +up-rust = "0.7" async-trait = "0.1" +tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread", "signal"] } +iceoryx2 = "0.7.0" +iceoryx2-bb-container = "0.7.0" +hostname = "0.4.1" +protobuf = "3.7.2" diff --git a/examples/common/constants.rs b/examples/common/constants.rs new file mode 100644 index 0000000..9a97beb --- /dev/null +++ b/examples/common/constants.rs @@ -0,0 +1,21 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use std::time::Duration; + +/// The source filter publisher examples will use +pub static SOURCE_FILTER_STR: &str = "up://device1/10AB/3/80CD"; + +/// A UMessage will be published at this frequency +#[allow(dead_code)] +pub static CYCLE_TIME: Duration = Duration::from_secs(1); diff --git a/examples/common/helpers.rs b/examples/common/helpers.rs new file mode 100644 index 0000000..2e4f8a3 --- /dev/null +++ b/examples/common/helpers.rs @@ -0,0 +1,38 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use up_rust::UMessage; + +/// Simply prints the [`UMessage`] instances source uri, sink uri, and payload to STDOUT +#[allow(dead_code)] +pub fn print_umessage(msg: &UMessage) { + let payload_utf8 = msg.payload.as_ref().map(|p| String::from_utf8_lossy(p)); + let (source_uri, sink_uri) = get_source_and_sink_uri(msg); + println!("Source Uri: {source_uri:?}"); + println!("Sink Uri: {sink_uri:?}"); + println!("Payload: {payload_utf8:?}"); +} + +fn get_source_and_sink_uri(msg: &UMessage) -> (Option, Option) { + let source_uri = msg + .attributes + .as_ref() + .and_then(|a| a.source.as_ref()) + .map(|s| s.to_uri(false)); + let sink_uri = msg + .attributes + .as_ref() + .and_then(|a| a.sink.as_ref()) + .map(|s| s.to_uri(false)); + (source_uri, sink_uri) +} diff --git a/examples/common/mod.rs b/examples/common/mod.rs new file mode 100644 index 0000000..343a9f0 --- /dev/null +++ b/examples/common/mod.rs @@ -0,0 +1,20 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +pub mod constants; +pub mod helpers; + +#[allow(unused_imports)] +pub use constants::*; +#[allow(unused_imports)] +pub use helpers::*; diff --git a/examples/publisher.rs b/examples/publisher.rs new file mode 100644 index 0000000..dcfbc7c --- /dev/null +++ b/examples/publisher.rs @@ -0,0 +1,45 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use std::{error::Error, str::FromStr}; +use up_rust::{UMessage, UMessageBuilder, UPayloadFormat, UTransport, UUri}; +use up_transport_iceoryx2_rust::{MessagingPattern, transport::UTransportIceoryx2}; + +mod common; +use crate::common::*; + +fn create_umessage(source_filter: &UUri, payload: String) -> Result> { + let umessage = UMessageBuilder::publish(source_filter.clone()) + .build_with_payload(payload.into_bytes(), UPayloadFormat::UPAYLOAD_FORMAT_TEXT)?; + Ok(umessage) +} + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + println!("uProtocol UTransportIceoryx2 publisher example"); + let source_filter = UUri::from_str(SOURCE_FILTER_STR).expect("Failed to create source UUri"); + let transport = UTransportIceoryx2::build(MessagingPattern::PublishSubscribe)?; + let mut counter: u64 = 0; + loop { + counter += 1; + let payload = format!("Hello, from uProtocols UTransport with Iceoryx2! Message {counter}"); + let umessage = create_umessage(&source_filter, payload)?; + let payload_memory_address = umessage.payload.as_ref().unwrap(); + println!("Publishing message!"); + print_umessage(&umessage); + println!("Payload Memory address: {payload_memory_address:p}"); + println!(); + transport.send(umessage).await?; + tokio::time::sleep(CYCLE_TIME).await; + } +} diff --git a/examples/subscriber.rs b/examples/subscriber.rs new file mode 100644 index 0000000..068db87 --- /dev/null +++ b/examples/subscriber.rs @@ -0,0 +1,49 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use async_trait::async_trait; +use std::{str::FromStr, sync::Arc}; +use up_rust::{UListener, UMessage, UTransport, UUri}; +use up_transport_iceoryx2_rust::{MessagingPattern, transport::UTransportIceoryx2}; + +mod common; +use crate::common::*; + +struct ConsolePrinter; + +#[async_trait] +impl UListener for ConsolePrinter { + async fn on_receive(&self, message: UMessage) { + let payload_memory_address = message.payload.as_ref().unwrap(); + println!("Received a message!"); + print_umessage(&message); + println!("Payload Memory address: {payload_memory_address:p}"); + println!(); + } +} + +#[tokio::main(flavor = "multi_thread")] +async fn main() -> Result<(), Box> { + println!("uProtocol UTransportIceoryx2 subscriber example"); + let source_filter = UUri::from_str(SOURCE_FILTER_STR).expect("Failed to create source UUri"); + let transport = UTransportIceoryx2::build(MessagingPattern::PublishSubscribe)?; + let ulistener = Arc::new(ConsolePrinter); + transport + .register_listener(&source_filter, None, ulistener) + .await?; + println!( + "Listening to message from source filter '{SOURCE_FILTER_STR}'. Press CTRL+C to kill this subscriber" + ); + println!("Waiting for messages..."); + tokio::signal::ctrl_c().await.map_err(Box::from) +} diff --git a/src/lib.rs b/src/lib.rs index c9c2ee6..2b5755b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,232 +1,45 @@ // ################################################################################ // Copyright (c) 2025 Contributors to the Eclipse Foundation -// +// // See the NOTICE file(s) distributed with this work for additional // information regarding copyright ownership. -// +// // This program and the accompanying materials are made available under the // terms of the Apache License Version 2.0 which is available at // https: //www.apache.org/licenses/LICENSE-2.0 -// +// // SPDX-License-Identifier: Apache-2.0 // ################################################################################ -use async_trait::async_trait; -use std::sync::Arc; -use up_rust::{UCode, UListener, UMessage, UStatus, UTransport, UUri}; - -/// This will be the main struct for our uProtocol transport. -/// It will hold the state necessary to communicate with iceoryx2, -/// such as the service connection and active listeners. -pub struct Iceoryx2Transport {} - -enum MessageType { - RpcRequest, - RpcResponseOrNotification, - Publish, -} - -// The #[async_trait] attribute enables async functions in our trait impl. -#[async_trait] -impl UTransport for Iceoryx2Transport { - async fn send(&self, _message: UMessage) -> Result<(), UStatus> { - todo!(); - } - - async fn register_listener( - &self, - _source_filter: &UUri, - _sink_filter: Option<&UUri>, - _listener: Arc, - ) -> Result<(), UStatus> { - todo!() - } - - async fn unregister_listener( - &self, - _source_filter: &UUri, - _sink_filter: Option<&UUri>, - _listener: Arc, - ) -> Result<(), UStatus> { - todo!() - } -} - -#[allow(dead_code)] -impl Iceoryx2Transport { - fn encode_uuri_segments(uuri: &UUri) -> Vec { - vec![ - uuri.authority_name.clone(), - Self::encode_hex(uuri.uentity_type_id() as u32), - Self::encode_hex(uuri.uentity_instance_id() as u32), - Self::encode_hex(uuri.uentity_major_version() as u32), - Self::encode_hex(uuri.resource_id() as u32), - ] - } - - fn encode_hex(value: u32) -> String { - format!("{:X}", value) - } - - /// Assumption: valid source and sink URIs provided: - /// send() makes use of UAttributesValidator - /// register_listener() and unregister_listener() use verify_filter_criteria() - /// Criteria for identification of message types can be found here: https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uattributes.adoc - fn determine_message_type(source: &UUri, sink: Option<&UUri>) -> Result { - let src_id = source.resource_id; - let sink_id = sink.map(|s| s.resource_id); - - if src_id == 0 { - if let Some(id) = sink_id { - if id >= 1 && id <= 0x7FFF { - return Ok(MessageType::RpcRequest); - } - } - } else if sink_id == Some(0) && src_id >= 1 && src_id <= 0xFFFE { - return Ok(MessageType::RpcResponseOrNotification); - } else if src_id >= 1 && src_id <= 0x7FFF { - return Ok(MessageType::Publish); - } - - Err(UStatus::fail_with_code( - UCode::INVALID_ARGUMENT, - "Unsupported UMessageType", - )) - } - - /// Called in send(), register_listener() and unregister_listener() - fn compute_service_name(source: &UUri, sink: Option<&UUri>) -> Result { - let join_segments = |segments: Vec| segments.join("/"); - - match Self::determine_message_type(source, sink)? { - MessageType::RpcRequest => { - let Some(sink_uri) = sink else { - return Err(UStatus::fail_with_code( - UCode::INVALID_ARGUMENT, - "sink required for RpcRequest", - )); - }; - let segments = Self::encode_uuri_segments(sink_uri); - Ok(format!("up/{}", join_segments(segments))) - } - MessageType::RpcResponseOrNotification => { - let Some(sink_uri) = sink else { - return Err(UStatus::fail_with_code( - UCode::INVALID_ARGUMENT, - "sink required for ResponseOrNotification", - )); - }; - let source_segments = Self::encode_uuri_segments(source); - let sink_segments = Self::encode_uuri_segments(sink_uri); - Ok(format!( - "up/{}/{}", - join_segments(source_segments), - join_segments(sink_segments) - )) - } - MessageType::Publish => { - let segments = Self::encode_uuri_segments(source); - Ok(format!("up/{}", join_segments(segments))) - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - fn test_uri(authority: &str, instance: u16, typ: u16, version: u8, resource: u16) -> UUri { - let entity_id = ((instance as u32) << 16) | (typ as u32); - UUri::try_from_parts(authority, entity_id, version, resource).unwrap() - } - - // performing successful tests for service name computation - - #[test] - // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"] - fn test_publish_service_name() { - let source = test_uri("device1", 0x0000, 0x10AB, 0x03, 0x7FFF); - - let name = Iceoryx2Transport::compute_service_name(&source, None).unwrap(); - assert_eq!(name, "up/device1/10AB/0/3/7FFF"); - } - - #[test] - // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"] - fn test_notification_service_name() { - let source = test_uri("device1", 0x0000, 0x10AB, 0x03, 0x80CD); - let sink = test_uri("device1", 0x0000, 0x30EF, 0x04, 0x0000); - let name = Iceoryx2Transport::compute_service_name(&source, Some(&sink)).unwrap(); - assert_eq!(name, "up/device1/10AB/0/3/80CD/device1/30EF/0/4/0"); - } - - #[test] - // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"] - fn test_rpc_request_service_name() { - let sink = test_uri("device1", 0x0004, 0x03AB, 0x03, 0x0000); - let reply_to = test_uri("device1", 0x0000, 0x00CD, 0x04, 0xB); - - let name = Iceoryx2Transport::compute_service_name(&sink, Some(&reply_to)).unwrap(); - assert_eq!(name, "up/device1/CD/0/4/B"); - } - - #[test] - // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"] - fn test_rpc_response_service_name() { - let source = test_uri("device1", 0x0000, 0x00CD, 0x04, 0xB); - let sink = test_uri("device1", 0x0004, 0x3AB, 0x3, 0x0000); - - let name = Iceoryx2Transport::compute_service_name(&source, Some(&sink)).unwrap(); - assert_eq!(name, "up/device1/CD/0/4/B/device1/3AB/4/3/0"); - } - - // performing failing tests for service name computation - - #[test] - // .specitem[dsn~up-attributes-request-source~1] - // .specitem[dsn~up-attributes-response-source~1] - // .specitem[dsn~up-attributes-notification-source~1] - fn test_missing_uri_error() { - let uuri = UUri::new(); - let result = Iceoryx2Transport::compute_service_name(&uuri, None); - - assert!(result.is_err()); - assert_eq!(result.unwrap_err().get_code(), UCode::INVALID_ARGUMENT); - } - - #[test] - //both source and sink have resource ID equal to 0 - // .specitem[dsn~up-attributes-request-source~1] - // .specitem[dsn~up-attributes-request-sink~1] - // .specitem[dsn~up-attributes-response-source~1] - // .specitem[dsn~up-attributes-response-sink~1] - fn test_fail_resource_id_error() { - let source = test_uri("device1", 0x0000, 0x00CD, 0x04, 0x000); - let sink = test_uri("device1", 0x0004, 0x3AB, 0x3, 0x0000); - let result = Iceoryx2Transport::compute_service_name(&source, Some(&sink)); - assert!(result.is_err_and(|err| err.get_code() == UCode::INVALID_ARGUMENT)); - } - - #[test] - //source has resource id=0 but missing sink - // .specitem[dsn~up-attributes-request-sink~1] - // .specitem[dsn~up-attributes-request-source~1] - fn test_fail_missing_sink_error() { - let source = test_uri("device1", 0x0000, 0x00CD, 0x04, 0x000); - let result = Iceoryx2Transport::compute_service_name(&source, None); - assert!(result.is_err_and(|err| err.get_code() == UCode::INVALID_ARGUMENT)); - } - - #[test] - //missing source URI - // .specitem[dsn~up-attributes-request-source~1] - // .specitem[dsn~up-attributes-response-source~1] - // .specitem[dsn~up-attributes-notification-source~1] - fn test_fail_missing_source_error() { - let uuri = UUri::new(); - let sink = test_uri("device1", 0x0004, 0x3AB, 0x3, 0x000); - let result = Iceoryx2Transport::compute_service_name(&uuri, Some(&sink)); - assert!(result.is_err_and(|err| err.get_code() == UCode::INVALID_ARGUMENT)); - } -} +pub const UPROTOCOL_MAJOR_VERSION: u8 = 0; + +use iceoryx2::{ + port::{publisher::Publisher, subscriber::Subscriber}, + prelude::{ServiceName, ZeroCopySend}, +}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, + sync::Arc, +}; +use tokio::sync::RwLock; +use up_rust::ComparableListener; + +use crate::uprotocolheader::UProtocolHeader; + +pub(crate) mod service_name_mapping; +pub mod transport; +pub(crate) mod uprotocolheader; +pub(crate) mod utransport_pubsub; +pub(crate) mod workers; + +pub use iceoryx2::prelude::MessagingPattern; + +pub trait BaseUserHeader: Debug + ZeroCopySend {} +pub trait BasePayload: Debug + ZeroCopySend {} + +pub(crate) type PublisherSet = + RwLock>>>; +pub(crate) type SubscriberSet = + RwLock>>>; +pub(crate) type ListenerMap = RwLock>>; diff --git a/src/service_name_mapping.rs b/src/service_name_mapping.rs new file mode 100644 index 0000000..7fbed96 --- /dev/null +++ b/src/service_name_mapping.rs @@ -0,0 +1,172 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use iceoryx2::prelude::{MessagingPattern, ServiceName}; +use up_rust::{UCode, UMessageType, UStatus, UUri}; + +fn encode_uuri_segments(uuri: &UUri) -> Vec { + vec![ + get_authority_name(uuri), + encode_hex(uuri.uentity_type_id() as u32), + encode_hex(uuri.uentity_instance_id() as u32), + encode_hex(uuri.uentity_major_version() as u32), + encode_hex(uuri.resource_id() as u32), + ] +} + +fn encode_hex(value: u32) -> String { + format!("{value:X}") +} + +fn get_authority_name(source_uuri: &UUri) -> String { + if source_uuri.authority_name.is_empty() { + match hostname::get().unwrap().into_string() { + Ok(hostname) => hostname, + Err(_) => "unknown".to_string(), + } + } else { + source_uuri.authority_name.clone() + } +} + +fn determine_message_type( + source: &UUri, + _sink: Option<&UUri>, + messaging_pattern: MessagingPattern, +) -> Result { + if is_a_publish(source, messaging_pattern) { + return Ok(UMessageType::UMESSAGE_TYPE_PUBLISH); + } + + Err(UStatus::fail_with_code( + UCode::INVALID_ARGUMENT, + "Could not determine a valid UMessageType from the provided UUri(s)", + )) +} + +fn is_a_publish(source: &UUri, messaging_pattern: MessagingPattern) -> bool { + !source.is_empty() && messaging_pattern == MessagingPattern::PublishSubscribe +} + +pub fn compute_service_name( + source: &UUri, + sink: Option<&UUri>, + messaging_pattern: MessagingPattern, +) -> Result { + let join_segments = |segments: Vec| segments.join("/"); + let message_type = determine_message_type(source, sink, messaging_pattern)?; + let service_name_str = match message_type { + UMessageType::UMESSAGE_TYPE_REQUEST => { + let Some(sink_uri) = sink else { + return Err(UStatus::fail_with_code( + UCode::INVALID_ARGUMENT, + format!( + "sink required for UMessageType {:?}", + UMessageType::UMESSAGE_TYPE_REQUEST + ), + )); + }; + let segments = encode_uuri_segments(sink_uri); + format!("up/{}", join_segments(segments)) + } + UMessageType::UMESSAGE_TYPE_RESPONSE | UMessageType::UMESSAGE_TYPE_NOTIFICATION => { + let Some(sink_uri) = sink else { + return Err(UStatus::fail_with_code( + UCode::INVALID_ARGUMENT, + format!( + "sink required for UMessageType {:?} or {:?}", + UMessageType::UMESSAGE_TYPE_RESPONSE, + UMessageType::UMESSAGE_TYPE_NOTIFICATION + ), + )); + }; + let source_segments = encode_uuri_segments(source); + let sink_segments = encode_uuri_segments(sink_uri); + format!( + "up/{}/{}", + join_segments(source_segments), + join_segments(sink_segments) + ) + } + UMessageType::UMESSAGE_TYPE_PUBLISH => { + let segments = encode_uuri_segments(source); + format!("up/{}", join_segments(segments)) + } + _ => { + return Err(UStatus::fail_with_code( + UCode::INVALID_ARGUMENT, + "Unsupported UMessageType for service name computation", + )); + } + }; + Ok(ServiceName::new(service_name_str.as_str()).expect("Failed to create service name")) +} + +#[cfg(test)] +mod tests { + use super::*; + use iceoryx2::prelude::MessagingPattern; + use up_rust::{UCode, UUri}; + + fn test_uri(authority: &str, instance: u16, typ: u16, version: u8, resource: u16) -> UUri { + let entity_id = ((instance as u32) << 16) | (typ as u32); + UUri::try_from_parts(authority, entity_id, version, resource).unwrap() + } + + // performing successful tests for service name computation + + #[test] + // [specitem,oft-sid="dsn~up-transport-iceoryx2-service-name~1",oft-needs="utest"] + fn test_publish_service_name() { + let source = test_uri("device1", 0x0000, 0x10AB, 0x03, 0x7FFF); + + let name = compute_service_name(&source, None, MessagingPattern::PublishSubscribe).unwrap(); + assert_eq!(name, "up/device1/10AB/0/3/7FFF"); + } + + // performing failing tests for service name computation + + #[test] + // .specitem[dsn~up-attributes-request-source~1] + // .specitem[dsn~up-attributes-response-source~1] + // .specitem[dsn~up-attributes-notification-source~1] + fn test_missing_uri_error() { + let uuri = UUri::new(); + let result = compute_service_name(&uuri, None, MessagingPattern::PublishSubscribe); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err().get_code(), UCode::INVALID_ARGUMENT); + } + + #[test] + //source has resource id=0 but missing sink + // .specitem[dsn~up-attributes-request-sink~1] + // .specitem[dsn~up-attributes-request-source~1] + fn test_fail_missing_sink_error() { + let source = test_uri("device1", 0x0000, 0x00CD, 0x04, 0x000); + let result = compute_service_name(&source, None, MessagingPattern::RequestResponse); + assert!(result.is_err_and(|err| err.get_code() == UCode::INVALID_ARGUMENT)); + } + + #[test] + //missing source URI + // .specitem[dsn~up-attributes-request-source~1] + // .specitem[dsn~up-attributes-response-source~1] + // .specitem[dsn~up-attributes-notification-source~1] + fn test_fail_missing_source_error() { + let uuri = UUri::new(); + let sink = test_uri("device1", 0x0004, 0x3AB, 0x3, 0x000); + let result = compute_service_name(&uuri, Some(&sink), MessagingPattern::PublishSubscribe); + assert!(result.is_err_and(|err| err.get_code() == UCode::INVALID_ARGUMENT)); + } +} diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..08b3591 --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,36 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use std::sync::Arc; + +use crate::utransport_pubsub::Iceoryx2PubSub; +use iceoryx2::prelude::MessagingPattern; +use up_rust::{UCode, UStatus, UTransport}; + +pub struct UTransportIceoryx2 {} + +impl UTransportIceoryx2 { + pub fn build(messaging_pattern: MessagingPattern) -> Result, UStatus> { + match messaging_pattern { + MessagingPattern::PublishSubscribe => Ok(UTransportIceoryx2::build_publish_subscribe()), + _ => Err(UStatus::fail_with_code( + UCode::UNIMPLEMENTED, + "Unimplemented messaging pattern", + )), + } + } + + fn build_publish_subscribe() -> Arc { + Iceoryx2PubSub::new() + } +} diff --git a/src/uprotocolheader.rs b/src/uprotocolheader.rs new file mode 100644 index 0000000..8598b47 --- /dev/null +++ b/src/uprotocolheader.rs @@ -0,0 +1,25 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use iceoryx2::prelude::ZeroCopySend; +use iceoryx2_bb_container::vec::FixedSizeVec; + +pub const MAX_FEASIBLE_UATTRIBUTES_SERIALIZED_LENGTH: usize = 1000; + +/// Also see [uAttributes Mapping to iceoryx2 user header](https://github.com/eclipse-uprotocol/up-spec/blob/0cc43c8afb7d7cbd3169ffe093be761c57308cef/up-l1/iceoryx2.adoc#411-uattributes-mapping-to-iceoryx2-user-header) +#[repr(C)] +#[derive(ZeroCopySend, Debug, Default)] +pub struct UProtocolHeader { + pub(crate) uprotocol_major_version: u8, + pub(crate) uattributes_serialized: FixedSizeVec, +} diff --git a/src/utransport_pubsub.rs b/src/utransport_pubsub.rs new file mode 100644 index 0000000..e70bb7d --- /dev/null +++ b/src/utransport_pubsub.rs @@ -0,0 +1,300 @@ +// // ################################################################################ +// // Copyright (c) 2025 Contributors to the Eclipse Foundation +// // +// // See the NOTICE file(s) distributed with this work for additional +// // information regarding copyright ownership. +// // +// // This program and the accompanying materials are made available under the +// // terms of the Apache License Version 2.0 which is available at +// // https: //www.apache.org/licenses/LICENSE-2.0 +// // +// // SPDX-License-Identifier: Apache-2.0 +// // ################################################################################ + +use async_trait::async_trait; +use iceoryx2::prelude::{AllocationStrategy, MessagingPattern}; +use iceoryx2::sample_mut::SampleMut; +use iceoryx2::{ + node::{Node, NodeBuilder}, + port::{publisher::Publisher, subscriber::Subscriber}, + prelude::ServiceName, + service::ipc_threadsafe, +}; +use iceoryx2_bb_container::vec::FixedSizeVec; +use protobuf::Message; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; +use up_rust::{ComparableListener, UCode, UListener, UMessage, UStatus, UTransport, UUri}; + +use crate::UPROTOCOL_MAJOR_VERSION; +use crate::uprotocolheader::MAX_FEASIBLE_UATTRIBUTES_SERIALIZED_LENGTH; +use crate::workers::dispatcher::Iceoryx2WorkerDispatcher; +use crate::{ + ListenerMap, PublisherSet, SubscriberSet, service_name_mapping::compute_service_name, + uprotocolheader::UProtocolHeader, +}; + +#[derive(Debug)] +pub struct Iceoryx2PubSub { + node: Node, + pub publishers: PublisherSet, + pub subscribers: SubscriberSet, + pub listeners: ListenerMap, +} + +impl Iceoryx2PubSub { + pub fn new() -> Arc { + let node = NodeBuilder::new() + .create::() + .expect("Failed to create Iceoryx2 Node"); + let transport = Arc::new(Self { + node, + publishers: RwLock::new(HashMap::new()), + subscribers: RwLock::new(HashMap::new()), + listeners: RwLock::new(HashMap::new()), + }); + Iceoryx2WorkerDispatcher::start_listener_worker(transport.clone()); + transport + } + + pub fn create_subscriber( + &self, + service_name: ServiceName, + ) -> Result, UStatus> { + let service = self + .node + .service_builder(&service_name) + .publish_subscribe::<[u8]>() + .user_header::() + .open_or_create() + .map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to create service: {e}")) + })?; + let subscriber = service.subscriber_builder().create().map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to create subscriber: {e}")) + })?; + Ok(subscriber) + } + + pub async fn get_or_create_publisher( + &self, + service_name: ServiceName, + ) -> Result>, UStatus> { + let publisher = self.get_publisher(service_name.clone()).await; + if let Some(publisher) = publisher { + return Ok(publisher); + } + self.create_publisher(service_name).await + } + + async fn create_publisher( + &self, + service_name: ServiceName, + ) -> Result>, UStatus> { + let service = self + .node + .service_builder(&service_name) + .publish_subscribe::<[u8]>() + .user_header::() + .open_or_create() + .map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to create service: {e}")) + })?; + + let publisher = service + .publisher_builder() + .allocation_strategy(AllocationStrategy::PowerOfTwo) + .create() + .map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to create publisher: {e}")) + })?; + let mut publishers = self.publishers.write().await; + publishers.insert(service_name.clone(), Arc::new(publisher)); + let publisher = publishers.get(&service_name).unwrap(); + Ok(publisher.clone()) + } + + async fn get_publisher( + &self, + service_name: ServiceName, + ) -> Option>> { + let publishers = self.publishers.read().await; + if publishers.contains_key(&service_name) { + let publisher = publishers.get(&service_name).unwrap(); + return Some(publisher.clone()); + } + None + } + + pub async fn relay(&self) -> Result<(), UStatus> { + let subscribers = self.subscribers.read().await; + for (service_name, subscriber) in subscribers.iter() { + match subscriber.receive() { + Ok(Some(sample)) => { + let payload = sample.payload(); + let umessage = UMessage::parse_from_bytes(payload).map_err(|e| { + UStatus::fail_with_code( + UCode::INTERNAL, + format!("Failed to deserialize UMessage: {}", e), + ) + })?; + if let Some(listeners_to_notify) = self.listeners.read().await.get(service_name) + { + for listener in listeners_to_notify.iter() { + let listener: &ComparableListener = listener; + let payload_clone = umessage.clone(); + listener.on_receive(payload_clone).await; + } + } + } + Ok(None) => continue, // No sample available + Err(e) => { + return Err(UStatus::fail_with_code( + UCode::INTERNAL, + format!("Failed to receive sample: {e}"), + )); + } + } + } + Ok(()) + } + + pub fn write_message_to_sample( + &self, + publisher: &Publisher, + message: UMessage, + ) -> Result, UStatus> { + let sample_size = message.compute_size(); + let mut sample = publisher + .loan_slice_uninit(sample_size as usize) + .map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to loan sample: {e}")) + })?; + let message_bytes = message + .write_to_bytes() + .map_err(|e| UStatus::fail_with_code(UCode::INTERNAL, e.to_string()))?; + let serialized_data = message_bytes.as_slice(); + let user_header: &mut UProtocolHeader = sample.user_header_mut(); + self.set_samples_user_header(user_header, message)?; + let sample_final = sample.write_from_slice(serialized_data); + Ok(sample_final) + } + + fn set_samples_user_header( + &self, + user_header: &mut UProtocolHeader, + message: UMessage, + ) -> Result<(), UStatus> { + user_header.uprotocol_major_version = UPROTOCOL_MAJOR_VERSION; + let serialized_uattributes = message + .attributes + .write_to_bytes() + .map_err(|e| UStatus::fail_with_code(UCode::INTERNAL, e.to_string()))?; + let mut fixed_sized_vec: FixedSizeVec = + FixedSizeVec::new(); + for byte in serialized_uattributes.iter() { + fixed_sized_vec.push(*byte); + } + user_header.uattributes_serialized = fixed_sized_vec; + Ok(()) + } +} + +#[async_trait] +impl UTransport for Iceoryx2PubSub { + /// ## DISCLAIMER + /// + /// This code is a prototype to make UMessage work with iceoryx2's ZeroCopySend system + /// + /// UMessage is not ZeroCopySend compatible as-is. If UMessages are sent + /// directly to an iceoryx2 publisher, it will compile. However, the + /// subscriber will receive a segmentation fault when receiving theUMessage + /// + /// See [ZeroCopySend's safety requirements](https://docs.rs/iceoryx2/latest/iceoryx2/prelude/trait.ZeroCopySend.html#safety) for more details. + /// + /// This essentially defeats the purpose of using iceoryx2 and + /// ZeroCopySend, as it copies the data into a fixed-size array and then out + /// of the array and back into a UMessage inside the `UTransport.send()` method + /// and the `UListener.on_receive()` method. The UTransport or UMessage + /// definition needs to be adjusted for this to truly be a zero-copy transport. + async fn send(&self, message: UMessage) -> Result<(), UStatus> { + let service_name = { + let source_filter = &message.attributes.source; + let sink_filter = message.attributes.sink.as_ref(); + compute_service_name( + source_filter, + sink_filter, + MessagingPattern::PublishSubscribe, + )? + }; + let publisher = self + .get_or_create_publisher(service_name) + .await + .map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to get publisher: {e}")) + })?; + let sample_final = self.write_message_to_sample(publisher.as_ref(), message)?; + sample_final.send().map_err(|e| { + UStatus::fail_with_code(UCode::INTERNAL, format!("Failed to send: {e}")) + })?; + Ok(()) + } + + async fn register_listener( + &self, + source_filter: &UUri, + sink_filter: Option<&UUri>, + listener: Arc, + ) -> Result<(), UStatus> { + up_rust::verify_filter_criteria(source_filter, sink_filter)?; + let service_name = compute_service_name( + source_filter, + sink_filter, + MessagingPattern::PublishSubscribe, + )?; + let has_subscriber = { + let subscribers = self.subscribers.read().await; + subscribers.contains_key(&service_name) + }; + // insert subscriber for service name if it does not already exist + if !has_subscriber { + let subscriber = self.create_subscriber(service_name.clone())?; + let mut subscribers = self.subscribers.write().await; + subscribers.insert(service_name.clone(), Arc::new(subscriber)); + } + // insert listener for service name if it does not already exist + if !self.listeners.read().await.contains_key(&service_name) { + let mut listeners = self.listeners.write().await; + listeners + .entry(service_name) + .or_default() + .insert(ComparableListener::new(listener)); + } + Ok(()) + } + + async fn unregister_listener( + &self, + source_filter: &UUri, + sink_filter: Option<&UUri>, + listener: Arc, + ) -> Result<(), UStatus> { + up_rust::verify_filter_criteria(source_filter, sink_filter)?; + let service_name = compute_service_name( + source_filter, + sink_filter, + MessagingPattern::PublishSubscribe, + )?; + let comparable_listener = ComparableListener::new(listener.clone()); + let mut listeners = self.listeners.write().await; + if let Some(existing_listeners) = listeners.get_mut(&service_name) { + existing_listeners.retain(|l| !l.eq(&comparable_listener)); + if existing_listeners.is_empty() { + let mut subscribers = self.subscribers.write().await; + subscribers.remove(&service_name); + } + } + Ok(()) + } +} diff --git a/src/workers/dispatcher.rs b/src/workers/dispatcher.rs new file mode 100644 index 0000000..a1d57d4 --- /dev/null +++ b/src/workers/dispatcher.rs @@ -0,0 +1,33 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use std::sync::{Arc, atomic::Ordering}; +use up_rust::UStatus; + +use crate::{utransport_pubsub::Iceoryx2PubSub, workers::worker::Iceoryx2Worker}; + +pub struct Iceoryx2WorkerDispatcher {} + +impl Iceoryx2WorkerDispatcher { + pub fn start_listener_worker(transport: Arc) { + let worker = Iceoryx2Worker::new(transport.clone()); + tokio::spawn(async { Iceoryx2WorkerDispatcher::run(worker).await }); + } + + async fn run(worker: Iceoryx2Worker) -> Result<(), UStatus> { + while worker.keep_alive.load(Ordering::Relaxed) { + worker.transport.relay().await?; + } + Ok(()) + } +} diff --git a/src/workers/mod.rs b/src/workers/mod.rs new file mode 100644 index 0000000..ffc7de1 --- /dev/null +++ b/src/workers/mod.rs @@ -0,0 +1,15 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +pub(crate) mod dispatcher; +pub(crate) mod worker; diff --git a/src/workers/worker.rs b/src/workers/worker.rs new file mode 100644 index 0000000..e35e646 --- /dev/null +++ b/src/workers/worker.rs @@ -0,0 +1,30 @@ +// ################################################################################ +// Copyright (c) 2025 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache License Version 2.0 which is available at +// https: //www.apache.org/licenses/LICENSE-2.0 +// +// SPDX-License-Identifier: Apache-2.0 +// ################################################################################ + +use std::sync::{Arc, atomic::AtomicBool}; + +use crate::utransport_pubsub::Iceoryx2PubSub; + +pub struct Iceoryx2Worker { + pub keep_alive: Arc, + pub transport: Arc, +} + +impl Iceoryx2Worker { + pub fn new(transport: Arc) -> Self { + Self { + keep_alive: Arc::new(AtomicBool::new(true)), + transport, + } + } +}