minot-cli 0.10.2__tar.gz → 0.11.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. {minot_cli-0.10.2 → minot_cli-0.11.1}/Cargo.lock +29 -29
  2. {minot_cli-0.10.2 → minot_cli-0.11.1}/Cargo.toml +17 -17
  3. {minot_cli-0.10.2 → minot_cli-0.11.1}/PKG-INFO +1 -1
  4. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/Cargo.toml +19 -0
  5. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/gen_c.rs +74 -24
  6. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/src/lib.rs +18 -0
  7. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_coord/src/lib.rs +4 -2
  8. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/README.md +17 -8
  9. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/src/lib.rs +9 -1
  10. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/lib.rs +12 -1
  11. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/net.rs +78 -54
  12. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/ship.rs +273 -173
  13. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/ros2_r2r.rs +2 -1
  14. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/README.md +0 -0
  15. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/build.rs +0 -0
  16. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/app.rs +0 -0
  17. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/coord.rs +0 -0
  18. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/event.rs +0 -0
  19. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/gen_c.rs.patch +0 -0
  20. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/gen_c.rs.patch2 +0 -0
  21. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/gen_c_tests.rs +0 -0
  22. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/handler.rs +0 -0
  23. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/minot.rs +0 -0
  24. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/runner.rs +0 -0
  25. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/tui.rs +0 -0
  26. {minot_cli-0.10.2 → minot_cli-0.11.1}/minot/src/ui.rs +0 -0
  27. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/Cargo.toml +0 -0
  28. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/README.md +0 -0
  29. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/examples/stream_probe.rs +0 -0
  30. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/src/qos.rs +0 -0
  31. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/test/metadata-v5.yml +0 -0
  32. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_bagread/test/metadata-v9.yml +0 -0
  33. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_coord/Cargo.toml +0 -0
  34. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_coord/README.md +0 -0
  35. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/Cargo.toml +0 -0
  36. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/default.rl +0 -0
  37. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/cleanup.rs +0 -0
  38. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/core/marina.rs +0 -0
  39. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/core/mod.rs +0 -0
  40. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/bag.rs +0 -0
  41. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/db3_transform.rs +0 -0
  42. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/mcap_transform.rs +0 -0
  43. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/mod.rs +0 -0
  44. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/pack.rs +0 -0
  45. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/io/transform_progress.rs +0 -0
  46. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/lib.rs +0 -0
  47. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/model/bag_ref.rs +0 -0
  48. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/model/mod.rs +0 -0
  49. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/progress.rs +0 -0
  50. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/driver.rs +0 -0
  51. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/folder.rs +0 -0
  52. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/gdrive.rs +0 -0
  53. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/gdrive_auth.rs +0 -0
  54. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/http.rs +0 -0
  55. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/minot/block_store.rs +0 -0
  56. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/minot/mod.rs +0 -0
  57. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/minot/protocol.rs +0 -0
  58. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/minot/remote_file.rs +0 -0
  59. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/mod.rs +0 -0
  60. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/ssh.rs +0 -0
  61. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/stub.rs +0 -0
  62. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/registry/transfer_progress.rs +0 -0
  63. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/storage/cache.rs +0 -0
  64. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/storage/config.rs +0 -0
  65. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_dataset/src/storage/mod.rs +0 -0
  66. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_flow/Cargo.toml +0 -0
  67. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_flow/src/lib.rs +0 -0
  68. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/Cargo.toml +0 -0
  69. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/README.md +0 -0
  70. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/buffer.rs +0 -0
  71. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/format.rs +0 -0
  72. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/init.rs +0 -0
  73. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/lib.rs +0 -0
  74. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/logger.rs +0 -0
  75. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/pane.rs +0 -0
  76. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_log/src/tests.rs +0 -0
  77. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_mtc/Cargo.toml +0 -0
  78. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_mtc/README.md +0 -0
  79. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_mtc/src/lib.rs +0 -0
  80. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/Cargo.toml +0 -0
  81. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/README.md +0 -0
  82. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/examples/c_interop_layout.rs +0 -0
  83. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/examples/c_interop_pc2.rs +0 -0
  84. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/examples/c_interop_test.rs +0 -0
  85. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/src/bin_layout.rs +0 -0
  86. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/src/c_interop_test.rs +0 -0
  87. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_net/src/lib.rs +0 -0
  88. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/Cargo.toml +0 -0
  89. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/be_node.rs +0 -0
  90. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/be_sub.rs +0 -0
  91. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/be_sub_drop.rs +0 -0
  92. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/be_sub_error.rs +0 -0
  93. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/pub.rs +0 -0
  94. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/smol.rs +0 -0
  95. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_pubsub/examples/sub.rs +0 -0
  96. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_scope/Cargo.toml +0 -0
  97. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_scope/README.md +0 -0
  98. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_scope/examples/test.rs +0 -0
  99. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_scope/src/lib.rs +0 -0
  100. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/Cargo.toml +0 -0
  101. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/README.md +0 -0
  102. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/client.rs +0 -0
  103. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/coordinator.rs +0 -0
  104. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_sea/src/network.rs +0 -0
  105. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/Cargo.toml +0 -0
  106. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/README.md +0 -0
  107. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/examples/bad_service_client.rs +0 -0
  108. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/examples/basic_service_client.rs +0 -0
  109. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/examples/basic_service_client2.rs +0 -0
  110. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/examples/basic_service_server.rs +0 -0
  111. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_service/src/lib.rs +0 -0
  112. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/Cargo.toml +0 -0
  113. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/README.md +0 -0
  114. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/hiroz.rs +0 -0
  115. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/lib.rs +0 -0
  116. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/mt_pubsub.rs +0 -0
  117. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/ros1.rs +0 -0
  118. {minot_cli-0.10.2 → minot_cli-0.11.1}/mt_wind/src/ros2.rs +0 -0
  119. {minot_cli-0.10.2 → minot_cli-0.11.1}/pyproject.toml +0 -0
@@ -441,9 +441,9 @@ dependencies = [
441
441
 
442
442
  [[package]]
443
443
  name = "bindgen"
444
- version = "0.71.1"
444
+ version = "0.72.1"
445
445
  source = "registry+https://github.com/rust-lang/crates.io-index"
446
- checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
446
+ checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
447
447
  dependencies = [
448
448
  "bitflags 2.13.1",
449
449
  "cexpr",
@@ -3802,7 +3802,7 @@ dependencies = [
3802
3802
 
3803
3803
  [[package]]
3804
3804
  name = "minot"
3805
- version = "0.10.2"
3805
+ version = "0.11.1"
3806
3806
  dependencies = [
3807
3807
  "anyhow",
3808
3808
  "bytesize",
@@ -3929,7 +3929,7 @@ dependencies = [
3929
3929
 
3930
3930
  [[package]]
3931
3931
  name = "mt_action"
3932
- version = "0.10.2"
3932
+ version = "0.11.1"
3933
3933
  dependencies = [
3934
3934
  "anyhow",
3935
3935
  "log",
@@ -3944,7 +3944,7 @@ dependencies = [
3944
3944
 
3945
3945
  [[package]]
3946
3946
  name = "mt_bagread"
3947
- version = "0.10.2"
3947
+ version = "0.11.1"
3948
3948
  dependencies = [
3949
3949
  "anyhow",
3950
3950
  "camino",
@@ -3965,7 +3965,7 @@ dependencies = [
3965
3965
 
3966
3966
  [[package]]
3967
3967
  name = "mt_coord"
3968
- version = "0.10.2"
3968
+ version = "0.11.1"
3969
3969
  dependencies = [
3970
3970
  "anyhow",
3971
3971
  "fs2",
@@ -3979,7 +3979,7 @@ dependencies = [
3979
3979
 
3980
3980
  [[package]]
3981
3981
  name = "mt_dataset"
3982
- version = "0.10.2"
3982
+ version = "0.11.1"
3983
3983
  dependencies = [
3984
3984
  "anyhow",
3985
3985
  "async-trait",
@@ -4022,7 +4022,7 @@ dependencies = [
4022
4022
 
4023
4023
  [[package]]
4024
4024
  name = "mt_flow"
4025
- version = "0.10.2"
4025
+ version = "0.11.1"
4026
4026
  dependencies = [
4027
4027
  "anyhow",
4028
4028
  "env_logger",
@@ -4036,7 +4036,7 @@ dependencies = [
4036
4036
 
4037
4037
  [[package]]
4038
4038
  name = "mt_log"
4039
- version = "0.10.2"
4039
+ version = "0.11.1"
4040
4040
  dependencies = [
4041
4041
  "env_filter 2.0.0",
4042
4042
  "env_logger",
@@ -4048,7 +4048,7 @@ dependencies = [
4048
4048
 
4049
4049
  [[package]]
4050
4050
  name = "mt_mtc"
4051
- version = "0.10.2"
4051
+ version = "0.11.1"
4052
4052
  dependencies = [
4053
4053
  "anyhow",
4054
4054
  "ariadne",
@@ -4061,7 +4061,7 @@ dependencies = [
4061
4061
 
4062
4062
  [[package]]
4063
4063
  name = "mt_net"
4064
- version = "0.10.2"
4064
+ version = "0.11.1"
4065
4065
  dependencies = [
4066
4066
  "anyhow",
4067
4067
  "rkyv 0.8.18",
@@ -4071,7 +4071,7 @@ dependencies = [
4071
4071
 
4072
4072
  [[package]]
4073
4073
  name = "mt_pubsub"
4074
- version = "0.10.2"
4074
+ version = "0.11.1"
4075
4075
  dependencies = [
4076
4076
  "anyhow",
4077
4077
  "async-trait",
@@ -4090,7 +4090,7 @@ dependencies = [
4090
4090
 
4091
4091
  [[package]]
4092
4092
  name = "mt_rat"
4093
- version = "0.10.2"
4093
+ version = "0.11.1"
4094
4094
  dependencies = [
4095
4095
  "anyhow",
4096
4096
  "async-trait",
@@ -4105,7 +4105,7 @@ dependencies = [
4105
4105
 
4106
4106
  [[package]]
4107
4107
  name = "mt_scope"
4108
- version = "0.10.2"
4108
+ version = "0.11.1"
4109
4109
  dependencies = [
4110
4110
  "anyhow",
4111
4111
  "env_logger",
@@ -4118,7 +4118,7 @@ dependencies = [
4118
4118
 
4119
4119
  [[package]]
4120
4120
  name = "mt_sea"
4121
- version = "0.10.2"
4121
+ version = "0.11.1"
4122
4122
  dependencies = [
4123
4123
  "anyhow",
4124
4124
  "async-trait",
@@ -4135,7 +4135,7 @@ dependencies = [
4135
4135
 
4136
4136
  [[package]]
4137
4137
  name = "mt_service"
4138
- version = "0.10.2"
4138
+ version = "0.11.1"
4139
4139
  dependencies = [
4140
4140
  "anyhow",
4141
4141
  "log",
@@ -4149,7 +4149,7 @@ dependencies = [
4149
4149
 
4150
4150
  [[package]]
4151
4151
  name = "mt_wind"
4152
- version = "0.10.2"
4152
+ version = "0.11.1"
4153
4153
  dependencies = [
4154
4154
  "anyhow",
4155
4155
  "async-trait",
@@ -5442,9 +5442,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
5442
5442
 
5443
5443
  [[package]]
5444
5444
  name = "r2r"
5445
- version = "0.9.5"
5445
+ version = "0.9.7"
5446
5446
  source = "registry+https://github.com/rust-lang/crates.io-index"
5447
- checksum = "b36b61a6252e8e7a24919b9877dacf1dc9f0ac4c2d0eba91c68b35c1b2283ed2"
5447
+ checksum = "1fe8623ef13a4f3c25f4ef407c6032eee9e1c95d0912d8ed758f616cbb95a6cd"
5448
5448
  dependencies = [
5449
5449
  "force-send-sync",
5450
5450
  "futures",
@@ -5470,9 +5470,9 @@ dependencies = [
5470
5470
 
5471
5471
  [[package]]
5472
5472
  name = "r2r_actions"
5473
- version = "0.9.5"
5473
+ version = "0.9.7"
5474
5474
  source = "registry+https://github.com/rust-lang/crates.io-index"
5475
- checksum = "5ca8488195951aaadef421b470c823cd095848bebf2004ff96ef5d351067eac1"
5475
+ checksum = "441f4aca2b1dfe553ab40f03bd869445d02dccd7ddff4c5f32e3a681fcd47c57"
5476
5476
  dependencies = [
5477
5477
  "bindgen",
5478
5478
  "r2r_common",
@@ -5482,9 +5482,9 @@ dependencies = [
5482
5482
 
5483
5483
  [[package]]
5484
5484
  name = "r2r_common"
5485
- version = "0.9.5"
5485
+ version = "0.9.7"
5486
5486
  source = "registry+https://github.com/rust-lang/crates.io-index"
5487
- checksum = "9838c2f88c9fd088f188eadbf9459371b1e576affd7b2df04c45cd1e44b79b99"
5487
+ checksum = "786a8b50036ed5d38a1dfbee73c711e270acd9a518b73351c66d541c1fa05422"
5488
5488
  dependencies = [
5489
5489
  "bindgen",
5490
5490
  "os_str_bytes",
@@ -5494,9 +5494,9 @@ dependencies = [
5494
5494
 
5495
5495
  [[package]]
5496
5496
  name = "r2r_macros"
5497
- version = "0.9.5"
5497
+ version = "0.9.7"
5498
5498
  source = "registry+https://github.com/rust-lang/crates.io-index"
5499
- checksum = "be0cda9778c21f78d2d593df41298c14b6530061dc70d2d31eb831f7fec8d648"
5499
+ checksum = "743bfcbea83d856d97d222b3c3545d4848ba81572f1d52dc5b6bff4899b8583d"
5500
5500
  dependencies = [
5501
5501
  "proc-macro2",
5502
5502
  "quote",
@@ -5505,9 +5505,9 @@ dependencies = [
5505
5505
 
5506
5506
  [[package]]
5507
5507
  name = "r2r_msg_gen"
5508
- version = "0.9.5"
5508
+ version = "0.9.7"
5509
5509
  source = "registry+https://github.com/rust-lang/crates.io-index"
5510
- checksum = "03d5e77385a6b959014c14c45a444e1fbff99afad70912e120c21c30e7a65488"
5510
+ checksum = "db410864937e12357cd65f3e82c6fb7c8372d0493f154886952522bad87817c9"
5511
5511
  dependencies = [
5512
5512
  "bindgen",
5513
5513
  "force-send-sync",
@@ -5523,9 +5523,9 @@ dependencies = [
5523
5523
 
5524
5524
  [[package]]
5525
5525
  name = "r2r_rcl"
5526
- version = "0.9.5"
5526
+ version = "0.9.7"
5527
5527
  source = "registry+https://github.com/rust-lang/crates.io-index"
5528
- checksum = "9e5c208b2fbe64a00a42a70391f708298b6765769d39987286f4b7c82539561e"
5528
+ checksum = "7c0420d9ea790fe2b4f66f7a88c8a7901491075d2fed4037010b03173392a22a"
5529
5529
  dependencies = [
5530
5530
  "bindgen",
5531
5531
  "paste",
@@ -4,12 +4,12 @@ members = ["minot", "mt_pubsub", "mt_coord", "mt_bagread", "mt_mtc", "mt_sea", "
4
4
  resolver = "3"
5
5
 
6
6
  [workspace.package]
7
- version = "0.10.2"
7
+ version = "0.11.1"
8
8
  keywords = ["robotics"]
9
9
  license = "MIT OR Apache-2.0"
10
- repository = "https://codeberg.org/stelzo/minot"
10
+ repository = "https://codeberg.org/stelzo/minot.git"
11
11
  edition = "2024"
12
- authors = ["Christopher Sieh <stelzo@steado.de>"]
12
+ authors = ["stelzo <stelzo@steado.de>"]
13
13
  categories = ["science::robotics"]
14
14
  documentation = "https://uos.github.io/minot"
15
15
  homepage = "https://uos.github.io/minot"
@@ -28,19 +28,19 @@ exclude = [
28
28
  ]
29
29
 
30
30
  [workspace.dependencies]
31
- mt_sea = { path = "mt_sea", version = "0.10.2", default-features = false }
32
- mt_coord = { path = "mt_coord", version = "0.10.2", default-features = false }
33
- mt_pubsub = { path = "mt_pubsub", version = "0.10.2", default-features = false }
34
- mt_service = { path = "mt_service", version = "0.10.2", default-features = false }
35
- mt_action = { path = "mt_action", version = "0.10.2", default-features = false }
36
- mt_scope = { path = "mt_scope", version = "0.10.2", default-features = false }
37
- mt_bagread = { path = "mt_bagread", version = "0.10.2", default-features = false }
38
- mt_net = { path = "mt_net", version = "0.10.2", default-features = false }
39
- mt_flow = { path = "mt_flow", version = "0.10.2", default-features = false }
40
- mt_dataset = { path = "mt_dataset", version = "0.10.2", default-features = false }
41
- mt_wind = { path = "mt_wind", version = "0.10.2", default-features = false }
42
- mt_mtc = { path = "mt_mtc", version = "0.10.2", default-features = false }
43
- mt_log = { path = "mt_log", version = "0.10.2", default-features = false }
31
+ mt_sea = { path = "mt_sea", version = "0.11.1", default-features = false }
32
+ mt_coord = { path = "mt_coord", version = "0.11.1", default-features = false }
33
+ mt_pubsub = { path = "mt_pubsub", version = "0.11.1", default-features = false }
34
+ mt_service = { path = "mt_service", version = "0.11.1", default-features = false }
35
+ mt_action = { path = "mt_action", version = "0.11.1", default-features = false }
36
+ mt_scope = { path = "mt_scope", version = "0.11.1", default-features = false }
37
+ mt_bagread = { path = "mt_bagread", version = "0.11.1", default-features = false }
38
+ mt_net = { path = "mt_net", version = "0.11.1", default-features = false }
39
+ mt_flow = { path = "mt_flow", version = "0.11.1", default-features = false }
40
+ mt_dataset = { path = "mt_dataset", version = "0.11.1", default-features = false }
41
+ mt_wind = { path = "mt_wind", version = "0.11.1", default-features = false }
42
+ mt_mtc = { path = "mt_mtc", version = "0.11.1", default-features = false }
43
+ mt_log = { path = "mt_log", version = "0.11.1", default-features = false }
44
44
 
45
45
  # Tools
46
46
  anyhow = "1.0"
@@ -88,7 +88,7 @@ rkyv = { version = "0.8.18" }
88
88
  serde = { version = "1.0", features = ["derive"] }
89
89
 
90
90
  # ROS
91
- r2r = "0.9.5"
91
+ r2r = "0.9.7"
92
92
  hiroz = { version = "0.2.0", git = "https://github.com/ZettaScaleLabs/hiroz.git", tag = "v0.2.0" }
93
93
  hiroz-msgs = { version = "0.2.0", git = "https://github.com/ZettaScaleLabs/hiroz.git", tag = "v0.2.0", default-features = false, features = [
94
94
  "std_msgs",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minot-cli
3
- Version: 0.10.2
3
+ Version: 0.11.1
4
4
  Classifier: Intended Audience :: Developers
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -109,6 +109,25 @@ assets = [
109
109
  ],
110
110
  ]
111
111
 
112
+ [package.metadata.binstall]
113
+ pkg-fmt = "tgz"
114
+ bin-dir = "minot-{ target }/min/{ bin }{ binary-ext }"
115
+
116
+ [package.metadata.binstall.overrides.x86_64-unknown-linux-gnu]
117
+ pkg-url = "https://uos-robotics.codeberg.page/ppa/bin/minot-{ version }-x86_64-unknown-linux-gnu.tar.gz"
118
+
119
+ [package.metadata.binstall.overrides.x86_64-unknown-linux-musl]
120
+ pkg-url = "https://uos-robotics.codeberg.page/ppa/bin/minot-{ version }-x86_64-unknown-linux-musl.tar.gz"
121
+
122
+ [package.metadata.binstall.overrides.aarch64-unknown-linux-gnu]
123
+ pkg-url = "https://uos-robotics.codeberg.page/ppa/bin/minot-{ version }-aarch64-unknown-linux-gnu.tar.gz"
124
+
125
+ [package.metadata.binstall.overrides.armv7-unknown-linux-gnueabihf]
126
+ pkg-url = "https://uos-robotics.codeberg.page/ppa/bin/minot-{ version }-armv7-unknown-linux-gnueabihf.tar.gz"
127
+
128
+ [package.metadata.binstall.overrides.aarch64-apple-darwin]
129
+ pkg-url = "https://uos-robotics.codeberg.page/ppa/bin/minot-{ version }-aarch64-apple-darwin.tar.gz"
130
+
112
131
  [package.metadata.generate-rpm]
113
132
 
114
133
  [[package.metadata.generate-rpm.assets]]
@@ -295,7 +295,7 @@ fn ffi_lib_rs() -> String {
295
295
  use std::ffi::{c_char, CStr, CString};
296
296
  use std::sync::{Arc, LazyLock, Mutex, OnceLock};
297
297
 
298
- use mt_pubsub::{Node, NodeConfig, Qos};
298
+ use mt_pubsub::{Durability, Node, NodeConfig, Qos, Reliability};
299
299
  pub mod generated;
300
300
 
301
301
  static RT: LazyLock<Mutex<Option<Arc<tokio::runtime::Runtime>>>> =
@@ -369,20 +369,31 @@ fn parse_level(filter: &str) -> Option<log::LevelFilter> {
369
369
  }
370
370
  }
371
371
 
372
- pub(crate) fn qos_from_i32(qos: i32) -> Option<Qos> {
373
- match qos {
374
- 0 => Some(Qos::Reliable),
375
- 1 => Some(Qos::BestEffort),
376
- _ => None,
377
- }
372
+ pub(crate) fn qos_from_i32(reliability: i32, durability: i32) -> Option<Qos> {
373
+ let reliability = match reliability {
374
+ 0 => Reliability::Reliable,
375
+ 1 => Reliability::TryReliable,
376
+ 2 => Reliability::BestEffort,
377
+ _ => return None,
378
+ };
379
+ let durability = match durability {
380
+ 0 => Durability::Volatile,
381
+ 1 => Durability::TransientLocal,
382
+ _ => return None,
383
+ };
384
+ Some(Qos::new(reliability, durability))
378
385
  }
379
386
 
380
387
  #[no_mangle]
381
- pub unsafe extern "C" fn mt_node_init(name: *const c_char, node_qos: i32) -> i32 {
388
+ pub unsafe extern "C" fn mt_node_init(
389
+ name: *const c_char,
390
+ reliability: i32,
391
+ durability: i32,
392
+ ) -> i32 {
382
393
  if name.is_null() {
383
394
  return -1;
384
395
  }
385
- let Some(mode) = qos_from_i32(node_qos) else {
396
+ let Some(mode) = qos_from_i32(reliability, durability) else {
386
397
  return -1;
387
398
  };
388
399
  let name_str = match CStr::from_ptr(name).to_str() {
@@ -492,11 +503,12 @@ fn go_core_file() -> String {
492
503
 
493
504
  /*
494
505
  #include <stdlib.h>
506
+ #include <stdint.h>
495
507
  #cgo darwin CFLAGS: -mmacosx-version-min=26.2
496
508
  #cgo darwin LDFLAGS: -L${SRCDIR}/../../ffi/target/release -lminot_ffi -framework Security -framework CoreFoundation -mmacosx-version-min=26.2
497
- #cgo linux LDFLAGS: -L${SRCDIR}/../../ffi/target/release -lminot_ffi -lpthread -ldl
509
+ #cgo linux LDFLAGS: -L${SRCDIR}/../../ffi/target/release -lminot_ffi -lpthread -ldl -lm
498
510
 
499
- extern int mt_node_init(const char* name, int node_qos);
511
+ extern int mt_node_init(const char* name, int reliability, int durability);
500
512
  extern void mt_sleep_ms(uint64_t ms);
501
513
  extern int mt_node_shutdown_cancelled(void);
502
514
  extern int mt_configure_logging(const char* filter);
@@ -512,13 +524,37 @@ import (
512
524
  "unsafe"
513
525
  )
514
526
 
515
- type Qos int
527
+ type Reliability int
516
528
 
517
529
  const (
518
- QosReliable Qos = 0
519
- QosBestEffort Qos = 1
530
+ ReliabilityReliable Reliability = iota
531
+ ReliabilityTryReliable
532
+ ReliabilityBestEffort
520
533
  )
521
534
 
535
+ type Durability int
536
+
537
+ const (
538
+ DurabilityVolatile Durability = iota
539
+ DurabilityTransientLocal
540
+ )
541
+
542
+ type Qos struct {
543
+ Reliability Reliability
544
+ Durability Durability
545
+ }
546
+
547
+ var (
548
+ QosReliable = Qos{Reliability: ReliabilityReliable}
549
+ QosTryReliable = Qos{Reliability: ReliabilityTryReliable}
550
+ QosBestEffort = Qos{Reliability: ReliabilityBestEffort}
551
+ )
552
+
553
+ func (q Qos) TransientLocal() Qos {
554
+ q.Durability = DurabilityTransientLocal
555
+ return q
556
+ }
557
+
522
558
  type Node struct {
523
559
  Name string
524
560
  shutdown chan struct{}
@@ -585,7 +621,7 @@ return cb, ok
585
621
  func NewNode(name string, nodeQos Qos) (*Node, error) {
586
622
  cName := C.CString(name)
587
623
  defer C.free(unsafe.Pointer(cName))
588
- if C.mt_node_init(cName, C.int(nodeQos)) != 0 {
624
+ if C.mt_node_init(cName, C.int(nodeQos.Reliability), C.int(nodeQos.Durability)) != 0 {
589
625
  return nil, fmt.Errorf("failed to initialize Minot node")
590
626
  }
591
627
  n := &Node{Name: name, shutdown: make(chan struct{})}
@@ -767,9 +803,9 @@ fn go_typed_message_spec(
767
803
 
768
804
  let root_c = format!("minot_{}", root_go.to_snake_case());
769
805
  out.push_str(&format!(
770
- "extern uintptr_t mt_create_{root_safe}_publisher(const char* topic, int qos);\n"
806
+ "extern uintptr_t mt_create_{root_safe}_publisher(const char* topic, int reliability, int durability);\n"
771
807
  ));
772
- out.push_str(&format!("extern uintptr_t mt_create_{root_safe}_subscriber(const char* topic, uint32_t queue_size, int qos, uintptr_t callback_handle);\n"));
808
+ out.push_str(&format!("extern uintptr_t mt_create_{root_safe}_subscriber(const char* topic, uint32_t queue_size, int reliability, int durability, uintptr_t callback_handle);\n"));
773
809
  out.push_str(&format!("extern int mt_{root_safe}_publisher_publish(uintptr_t publisher_handle, const {root_c}* message);\n"));
774
810
  out.push_str(&format!("extern void mt_register_{root_safe}_callback(uintptr_t handle, void (*callback)(const {root_c}*, uintptr_t));\n"));
775
811
  out.push_str(&format!(
@@ -781,7 +817,7 @@ fn go_typed_message_spec(
781
817
  out.push_str(&format!("extern int mt_{root_safe}_service_request(uintptr_t client_handle, const {root_c}* request, uint64_t timeout_ms, {root_c}** out_response);\n"));
782
818
  out.push_str(&format!("extern void mt_register_{root_safe}_service_callback(uintptr_t handle, int (*callback)(const {root_c}* request, {root_c}* response, uintptr_t));\n"));
783
819
  out.push_str(&format!("extern uintptr_t mt_create_{root_safe}_service_server(const char* topic, uintptr_t callback_handle);\n"));
784
- out.push_str(&format!("extern uintptr_t mt_create_{root_safe}_action_client(const char* topic, int feedback_qos);\n"));
820
+ out.push_str(&format!("extern uintptr_t mt_create_{root_safe}_action_client(const char* topic, int reliability, int durability);\n"));
785
821
  out.push_str(&format!("extern int mt_{root_safe}_action_wait_for_server(uintptr_t client_handle, uint64_t timeout_ms);\n"));
786
822
  out.push_str(&format!("extern int mt_{root_safe}_action_send_goal(uintptr_t client_handle, uint64_t goal_id, const {root_c}* goal, uint64_t timeout_ms, int* out_accepted, uint64_t* out_time_ms);\n"));
787
823
  out.push_str(&format!("extern int mt_{root_safe}_action_cancel_goal(uintptr_t client_handle, uint64_t goal_id, uint64_t timeout_ms);\n"));
@@ -866,10 +902,10 @@ fn go_typed_message_spec(
866
902
  .get(root)
867
903
  .ok_or_else(|| anyhow!("missing root type"))?;
868
904
  let root_c_type = format!("C.minot_{}", root_type.to_snake_case());
869
- out.push_str(&format!("func create{root_go}Publisher(topic string, qos Qos) (uintptr, error) {{\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\th := C.mt_create_{root_safe}_publisher(cTopic, C.int(qos))\n\tif h == 0 {{\n\t\treturn 0, fmt.Errorf(\"failed to create publisher for topic %s\", topic)\n\t}}\n\treturn uintptr(h), nil\n}}\n\n"));
905
+ out.push_str(&format!("func create{root_go}Publisher(topic string, qos Qos) (uintptr, error) {{\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\th := C.mt_create_{root_safe}_publisher(cTopic, C.int(qos.Reliability), C.int(qos.Durability))\n\tif h == 0 {{\n\t\treturn 0, fmt.Errorf(\"failed to create publisher for topic %s\", topic)\n\t}}\n\treturn uintptr(h), nil\n}}\n\n"));
870
906
  out.push_str(&format!("func publish{root_go}(handle uintptr, msg {root_type}) error {{\n\tcMsg := toC{root_type}(msg)\n\tdefer freeC{root_type}(&cMsg)\n\tif C.mt_{root_safe}_publisher_publish(C.uintptr_t(handle), (*{root_c_type})(unsafe.Pointer(&cMsg))) != 0 {{\n\t\treturn fmt.Errorf(\"publish failed\")\n\t}}\n\treturn nil\n}}\n\n"));
871
907
  out.push_str(&format!("//export Go{root_go}Callback\nfunc Go{root_go}Callback(message *{root_c_type}, handle C.uintptr_t) {{\n\tif cb, ok := GetHandler(uintptr(handle)); ok {{\n\t\tif typedCb, ok := cb.(func({root_type})); ok {{\n\t\t\tif message == nil {{\n\t\t\t\tvar zero {root_type}\n\t\t\t\ttypedCb(zero)\n\t\t\t\treturn\n\t\t\t}}\n\t\t\ttypedCb(cToGo{root_type}(*message))\n\t\t}}\n\t}}\n}}\n\n"));
872
- out.push_str(&format!("func create{root_go}Subscriber(topic string, queueSize int, qos Qos, callback func({root_type})) (uintptr, error) {{\n\th := RegisterHandler(callback)\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\tC.registerGo{root_go}Callback(C.uintptr_t(h))\n\ts := C.mt_create_{root_safe}_subscriber(cTopic, C.uint32_t(queueSize), C.int(qos), C.uintptr_t(h))\n\tif s == 0 {{\n\t\treturn 0, fmt.Errorf(\"failed to create subscriber for topic %s\", topic)\n\t}}\n\treturn uintptr(s), nil\n}}\n\n"));
908
+ out.push_str(&format!("func create{root_go}Subscriber(topic string, queueSize int, qos Qos, callback func({root_type})) (uintptr, error) {{\n\th := RegisterHandler(callback)\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\tC.registerGo{root_go}Callback(C.uintptr_t(h))\n\ts := C.mt_create_{root_safe}_subscriber(cTopic, C.uint32_t(queueSize), C.int(qos.Reliability), C.int(qos.Durability), C.uintptr_t(h))\n\tif s == 0 {{\n\t\treturn 0, fmt.Errorf(\"failed to create subscriber for topic %s\", topic)\n\t}}\n\treturn uintptr(s), nil\n}}\n\n"));
873
909
  out.push_str(&format!("var {root_go}Spec = MessageSpec[{root_type}]{{\n\tCreatePublisher: create{root_go}Publisher,\n\tPublish: publish{root_go},\n\tCreateSubscriber: create{root_go}Subscriber,\n}}\n\n"));
874
910
  out.push_str(&format!("func (n *Node) New{root_go}Publisher(topic string, qos Qos) (*Publisher[{root_type}], error) {{\n\treturn newPublisher(topic, qos, {root_go}Spec)\n}}\n\n"));
875
911
  out.push_str(&format!("func (n *Node) New{root_go}Subscriber(topic string, queueSize int, qos Qos, callback func({root_type})) (*Subscriber[{root_type}], error) {{\n\treturn newSubscriber(topic, queueSize, qos, {root_go}Spec, callback)\n}}\n"));
@@ -887,7 +923,7 @@ fn go_typed_message_spec(
887
923
  out.push_str(&format!(
888
924
  "type {root_go}ActionClient struct {{\n\thandle uintptr\n\ttopic string\n}}\n\n"
889
925
  ));
890
- out.push_str(&format!("func (n *Node) New{root_go}ActionClient(topic string, feedbackQos Qos) (*{root_go}ActionClient, error) {{\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\th := C.mt_create_{root_safe}_action_client(cTopic, C.int(feedbackQos))\n\tif h == 0 {{\n\t\treturn nil, fmt.Errorf(\"failed to create action client for topic %s\", topic)\n\t}}\n\treturn &{root_go}ActionClient{{handle: uintptr(h), topic: topic}}, nil\n}}\n\n"));
926
+ out.push_str(&format!("func (n *Node) New{root_go}ActionClient(topic string, feedbackQos Qos) (*{root_go}ActionClient, error) {{\n\tcTopic := C.CString(topic)\n\tdefer C.free(unsafe.Pointer(cTopic))\n\th := C.mt_create_{root_safe}_action_client(cTopic, C.int(feedbackQos.Reliability), C.int(feedbackQos.Durability))\n\tif h == 0 {{\n\t\treturn nil, fmt.Errorf(\"failed to create action client for topic %s\", topic)\n\t}}\n\treturn &{root_go}ActionClient{{handle: uintptr(h), topic: topic}}, nil\n}}\n\n"));
891
927
  out.push_str(&format!("func (c *{root_go}ActionClient) WaitForServer(timeoutMs uint64) bool {{\n\treturn C.mt_{root_safe}_action_wait_for_server(C.uintptr_t(c.handle), C.uint64_t(timeoutMs)) != 0\n}}\n\n"));
892
928
  out.push_str(&format!("func (c *{root_go}ActionClient) SendGoal(goalID uint64, goal {root_type}, timeoutMs uint64) (bool, uint64, error) {{\n\tcGoal := toC{root_type}(goal)\n\tdefer freeC{root_type}(&cGoal)\n\tvar accepted C.int\n\tvar timeMs C.uint64_t\n\tres := C.mt_{root_safe}_action_send_goal(C.uintptr_t(c.handle), C.uint64_t(goalID), (*{root_c_type})(unsafe.Pointer(&cGoal)), C.uint64_t(timeoutMs), &accepted, &timeMs)\n\tif res != 0 {{\n\t\treturn false, 0, fmt.Errorf(\"send goal failed\")\n\t}}\n\treturn accepted != 0, uint64(timeMs), nil\n}}\n\n"));
893
929
  out.push_str(&format!("func (c *{root_go}ActionClient) CancelGoal(goalID uint64, timeoutMs uint64) error {{\n\tif C.mt_{root_safe}_action_cancel_goal(C.uintptr_t(c.handle), C.uint64_t(goalID), C.uint64_t(timeoutMs)) != 0 {{\n\t\treturn fmt.Errorf(\"cancel goal failed\")\n\t}}\n\treturn nil\n}}\n\n"));
@@ -1012,15 +1048,15 @@ fn rust_typed_message_module(
1012
1048
  out.push_str(&format!("#[no_mangle]\npub extern \"C\" fn mt_register_{}_callback(handle: usize, callback: extern \"C\" fn(*const {}, usize)) {{\n {}_CALLBACKS.lock().unwrap().insert(handle, callback);\n}}\n\n", root_safe, root_c, root_safe.to_ascii_uppercase()));
1013
1049
  out.push_str(&format!("#[no_mangle]\npub extern \"C\" fn mt_register_{}_service_callback(handle: usize, callback: extern \"C\" fn(*const {}, *mut {}, usize) -> i32) {{\n {}_SERVICE_CALLBACKS.lock().unwrap().insert(handle, callback);\n}}\n\n", root_safe, root_c, root_c, root_safe.to_ascii_uppercase()));
1014
1050
 
1015
- out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_publisher(topic: *const c_char, qos: i32) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(qos) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let publisher = match rt.block_on(async {{ node.create_publisher::<{}>(topic_str, q).await }}) {{ Ok(p) => p, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n {}_PUBLISHERS.lock().unwrap().insert(handle, publisher);\n handle\n}}\n\n", root_safe, root_mod_type, root_safe.to_ascii_uppercase()));
1051
+ out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_publisher(topic: *const c_char, reliability: i32, durability: i32) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(reliability, durability) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let publisher = match rt.block_on(async {{ node.create_publisher::<{}>(topic_str, q).await }}) {{ Ok(p) => p, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n {}_PUBLISHERS.lock().unwrap().insert(handle, publisher);\n handle\n}}\n\n", root_safe, root_mod_type, root_safe.to_ascii_uppercase()));
1016
1052
 
1017
1053
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_{}_publisher_publish(publisher_handle: usize, message: *const {}) -> i32 {{\n let rust_msg = match {}_to_rust(message) {{ Ok(m) => m, Err(_) => return -3 }};\n let publishers = {}_PUBLISHERS.lock().unwrap();\n let publisher = match publishers.get(&publisher_handle) {{ Some(p) => p.clone(), None => return -1 }};\n drop(publishers);\n let publish_fut = async move {{ publisher.publish(&rust_msg).await }};\n let result = if tokio::runtime::Handle::try_current().is_ok() {{ tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(publish_fut)) }} else {{ let rt_lock = RT.lock().unwrap(); let Some(rt) = rt_lock.as_ref().cloned() else {{ return -4; }}; drop(rt_lock); rt.block_on(publish_fut) }};\n if result.is_ok() {{ 0 }} else {{ -2 }}\n}}\n\n", root_safe, root_c, root_go.to_snake_case(), root_safe.to_ascii_uppercase()));
1018
1054
 
1019
- out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_subscriber(topic: *const c_char, queue_size: u32, qos: i32, callback_handle: usize) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(qos) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let mut subscriber = match rt.block_on(async {{ node.create_subscriber::<{}>(topic_str, queue_size as usize, q).await }}) {{ Ok(s) => s, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n rt.spawn(async move {{\n while let Some(msg) = subscriber.next().await {{\n let c_msg = {}_from_rust(&msg);\n let cb_opt = {{ let map = {}_CALLBACKS.lock().unwrap(); map.get(&callback_handle).copied() }};\n if let Some(cb) = cb_opt {{\n let boxed = Box::new(c_msg);\n cb(Box::as_ref(&boxed), callback_handle);\n let raw = Box::into_raw(boxed);\n mt_free_{}_message(raw);\n }}\n }}\n }});\n handle\n}}\n", root_safe, root_mod_type, root_go.to_snake_case(), root_safe.to_ascii_uppercase(), root_safe));
1055
+ out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_subscriber(topic: *const c_char, queue_size: u32, reliability: i32, durability: i32, callback_handle: usize) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(reliability, durability) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let mut subscriber = match rt.block_on(async {{ node.create_subscriber::<{}>(topic_str, queue_size as usize, q).await }}) {{ Ok(s) => s, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n rt.spawn(async move {{\n while let Some(msg) = subscriber.next().await {{\n let c_msg = {}_from_rust(&msg);\n let cb_opt = {{ let map = {}_CALLBACKS.lock().unwrap(); map.get(&callback_handle).copied() }};\n if let Some(cb) = cb_opt {{\n let boxed = Box::new(c_msg);\n cb(Box::as_ref(&boxed), callback_handle);\n let raw = Box::into_raw(boxed);\n mt_free_{}_message(raw);\n }}\n }}\n }});\n handle\n}}\n", root_safe, root_mod_type, root_go.to_snake_case(), root_safe.to_ascii_uppercase(), root_safe));
1020
1056
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_service_client(topic: *const c_char) -> usize {{\n if topic.is_null() {{ return 0; }}\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let client = match rt.block_on(async {{ ServiceClient::<{}, {}>::new(node, topic_str).await }}) {{ Ok(c) => c, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n {}_SERVICE_CLIENTS.lock().unwrap().insert(handle, client);\n handle\n}}\n\n", root_safe, root_mod_type, root_mod_type, root_safe.to_ascii_uppercase()));
1021
1057
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_{}_service_request(client_handle: usize, request: *const {}, timeout_ms: u64, out_response: *mut *mut {}) -> i32 {{\n if out_response.is_null() {{ return -1; }}\n let req = match {}_to_rust(request) {{ Ok(v) => v, Err(_) => return -2 }};\n let clients = {}_SERVICE_CLIENTS.lock().unwrap();\n let Some(client) = clients.get(&client_handle) else {{ return -3; }};\n let fut = client.request(req, Some(Duration::from_millis(timeout_ms)));\n let result = if tokio::runtime::Handle::try_current().is_ok() {{ tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut)) }} else {{ let rt_lock = RT.lock().unwrap(); let Some(rt) = rt_lock.as_ref().cloned() else {{ return -4; }}; drop(rt_lock); rt.block_on(fut) }};\n match result {{\n Ok(resp) => {{\n let c_resp = {}_from_rust(&resp);\n *out_response = Box::into_raw(Box::new(c_resp));\n 0\n }}\n Err(_) => -5,\n }}\n}}\n\n", root_safe, root_c, root_c, root_go.to_snake_case(), root_safe.to_ascii_uppercase(), root_go.to_snake_case()));
1022
1058
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_service_server(topic: *const c_char, callback_handle: usize) -> usize {{\n if topic.is_null() {{ return 0; }}\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let cb_opt = {{ let cbs = {}_SERVICE_CALLBACKS.lock().unwrap(); cbs.get(&callback_handle).copied() }};\n let Some(cb) = cb_opt else {{ return 0; }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let server = match rt.block_on(async {{ ServiceServer::<{}, {}>::new(node, topic_str).await }}) {{ Ok(s) => s, Err(_) => return 0 }};\n let server_clone = server.clone();\n rt.spawn(async move {{\n let callback = std::sync::Arc::new(move |req: {}| {{\n let c_req = {}_from_rust(&req);\n let mut c_resp: {} = unsafe {{ std::mem::zeroed() }};\n let rc = cb(&c_req as *const {}, &mut c_resp as *mut {}, callback_handle);\n std::future::ready(if rc != 0 {{\n Err(\"service callback failed\".to_string())\n }} else {{\n match {}_to_rust(&c_resp as *const {}) {{\n Ok(v) => Ok(v),\n Err(_) => Err(\"service callback response conversion failed\".to_string()),\n }}\n }})\n }});\n let _ = ServiceServer::start(server_clone, callback).await;\n }});\n let handle = rand::random::<usize>();\n {}_SERVICE_SERVERS.lock().unwrap().insert(handle, server);\n handle\n}}\n\n", root_safe, root_safe.to_ascii_uppercase(), root_mod_type, root_mod_type, root_mod_type, root_go.to_snake_case(), root_c, root_c, root_c, root_go.to_snake_case(), root_c, root_safe.to_ascii_uppercase()));
1023
- out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_action_client(topic: *const c_char, feedback_qos: i32) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(feedback_qos) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let client = match rt.block_on(async {{ ActionClient::<{}, {}, {}>::new(node, topic_str, q).await }}) {{ Ok(c) => c, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n {}_ACTION_CLIENTS.lock().unwrap().insert(handle, client);\n handle\n}}\n\n", root_safe, root_mod_type, root_mod_type, root_mod_type, root_safe.to_ascii_uppercase()));
1059
+ out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_create_{}_action_client(topic: *const c_char, reliability: i32, durability: i32) -> usize {{\n if topic.is_null() {{ return 0; }}\n let Some(q) = qos_from_i32(reliability, durability) else {{ return 0; }};\n let topic_str = match CStr::from_ptr(topic).to_str() {{ Ok(s) => s.to_owned(), Err(_) => return 0 }};\n let node_lock = NODE.lock().unwrap();\n let Some(node) = node_lock.as_ref().cloned() else {{ return 0; }};\n drop(node_lock);\n let rt_lock = RT.lock().unwrap();\n let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }};\n drop(rt_lock);\n let client = match rt.block_on(async {{ ActionClient::<{}, {}, {}>::new(node, topic_str, q).await }}) {{ Ok(c) => c, Err(_) => return 0 }};\n let handle = rand::random::<usize>();\n {}_ACTION_CLIENTS.lock().unwrap().insert(handle, client);\n handle\n}}\n\n", root_safe, root_mod_type, root_mod_type, root_mod_type, root_safe.to_ascii_uppercase()));
1024
1060
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_{}_action_wait_for_server(client_handle: usize, timeout_ms: u64) -> i32 {{\n let clients = {}_ACTION_CLIENTS.lock().unwrap();\n let Some(client) = clients.get(&client_handle) else {{ return 0; }};\n let fut = ActionClient::<{}, {}, {}>::wait_for_action_server(client.clone(), Duration::from_millis(timeout_ms));\n let ready = if tokio::runtime::Handle::try_current().is_ok() {{ tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut)) }} else {{ let rt_lock = RT.lock().unwrap(); let Some(rt) = rt_lock.as_ref().cloned() else {{ return 0; }}; drop(rt_lock); rt.block_on(fut) }};\n if ready {{ 1 }} else {{ 0 }}\n}}\n\n", root_safe, root_safe.to_ascii_uppercase(), root_mod_type, root_mod_type, root_mod_type));
1025
1061
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_{}_action_send_goal(client_handle: usize, goal_id: u64, goal: *const {}, timeout_ms: u64, out_accepted: *mut i32, out_time_ms: *mut u64) -> i32 {{\n if out_accepted.is_null() || out_time_ms.is_null() {{ return -1; }}\n let goal_msg = match {}_to_rust(goal) {{ Ok(v) => v, Err(_) => return -2 }};\n let clients = {}_ACTION_CLIENTS.lock().unwrap();\n let Some(client) = clients.get(&client_handle) else {{ return -3; }};\n let req = ActionSendGoalRequest {{ id: goal_id, goal: goal_msg }};\n let fut = ActionClient::<{}, {}, {}>::send_goal(client.clone(), req, Some(Duration::from_millis(timeout_ms)));\n let result = if tokio::runtime::Handle::try_current().is_ok() {{ tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut)) }} else {{ let rt_lock = RT.lock().unwrap(); let Some(rt) = rt_lock.as_ref().cloned() else {{ return -4; }}; drop(rt_lock); rt.block_on(fut) }};\n match result {{\n Ok(resp) => {{\n *out_accepted = if resp.accepted {{ 1 }} else {{ 0 }};\n *out_time_ms = resp.time.unwrap_or(0) as u64;\n 0\n }}\n Err(_) => -5,\n }}\n}}\n\n", root_safe, root_c, root_go.to_snake_case(), root_safe.to_ascii_uppercase(), root_mod_type, root_mod_type, root_mod_type));
1026
1062
  out.push_str(&format!("#[no_mangle]\npub unsafe extern \"C\" fn mt_{}_action_cancel_goal(client_handle: usize, goal_id: u64, timeout_ms: u64) -> i32 {{\n let clients = {}_ACTION_CLIENTS.lock().unwrap();\n let Some(client) = clients.get(&client_handle) else {{ return -1; }};\n let req = ActionCancelGoalRequest {{ id: Some(goal_id), time: None }};\n let fut = ActionClient::<{}, {}, {}>::cancel_goal(client.clone(), req, Some(Duration::from_millis(timeout_ms)));\n let result = if tokio::runtime::Handle::try_current().is_ok() {{ tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut)) }} else {{ let rt_lock = RT.lock().unwrap(); let Some(rt) = rt_lock.as_ref().cloned() else {{ return -2; }}; drop(rt_lock); rt.block_on(fut) }};\n if result.is_ok() {{ 0 }} else {{ -3 }}\n}}\n\n", root_safe, root_safe.to_ascii_uppercase(), root_mod_type, root_mod_type, root_mod_type));
@@ -2421,6 +2457,20 @@ fn c_and_go_scalar(ty: &Type) -> Result<(&'static str, &'static str)> {
2421
2457
  mod tests {
2422
2458
  use super::*;
2423
2459
 
2460
+ #[test]
2461
+ fn go_qos_combines_reliability_and_durability() {
2462
+ let go = go_core_file();
2463
+ assert!(go.contains("type Reliability int"));
2464
+ assert!(go.contains("type Durability int"));
2465
+ assert!(go.contains("type Qos struct"));
2466
+ assert!(go.contains("func (q Qos) TransientLocal() Qos"));
2467
+
2468
+ let ffi = ffi_lib_rs();
2469
+ assert!(ffi.contains("1 => Reliability::TryReliable"));
2470
+ assert!(ffi.contains("1 => Durability::TransientLocal"));
2471
+ assert!(ffi.contains("Some(Qos::new(reliability, durability))"));
2472
+ }
2473
+
2424
2474
  #[test]
2425
2475
  fn test_parse_and_generate_simple_struct() {
2426
2476
  let input = "
@@ -139,6 +139,9 @@ where
139
139
  where
140
140
  E: de::Error,
141
141
  {
142
+ if value.trim().is_empty() {
143
+ return Ok(Vec::new());
144
+ }
142
145
  serde_yml::from_str(value).map_err(de::Error::custom)
143
146
  }
144
147
 
@@ -1629,6 +1632,21 @@ rosbag2_bagfile_information:
1629
1632
  );
1630
1633
  }
1631
1634
 
1635
+ #[test]
1636
+ fn empty_qos_profile_string_is_an_empty_list() {
1637
+ let metadata: TopicMetadata = serde_yml::from_str(
1638
+ r#"
1639
+ name: /ouster/points
1640
+ type: sensor_msgs/msg/PointCloud2
1641
+ serialization_format: cdr
1642
+ offered_qos_profiles: ""
1643
+ "#,
1644
+ )
1645
+ .expect("empty QoS profile string should parse");
1646
+
1647
+ assert!(metadata.offered_qos_profiles.is_empty());
1648
+ }
1649
+
1632
1650
  #[test]
1633
1651
  fn both_formats_parse_without_errors() {
1634
1652
  let v5_result = std::fs::read_to_string("test/metadata-v5.yml").and_then(|contents| {
@@ -5,7 +5,7 @@ use mt_sea::net::{self, Packet};
5
5
  use mt_sea::{coordinator::CoordinatorImpl, net::PacketKind};
6
6
 
7
7
  use mt_net::{ActionPlan, COMPARE_NODE_NAME, RatPubRegisterKind, Rules, VariableHuman};
8
- use mt_sea::{Coordinator, Qos, ShipKind, WindData};
8
+ use mt_sea::{Coordinator, Qos, Reliability, ShipKind, WindData};
9
9
 
10
10
  const EMBEDDED_COORDINATOR_READY_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
11
11
 
@@ -1095,7 +1095,9 @@ fn run_coordinator_with_ready(
1095
1095
  node_mode,
1096
1096
  } => {
1097
1097
  // Track BE publishers
1098
- if kind == net::RatPubRegisterKind::Publish && node_mode == Qos::BestEffort {
1098
+ if kind == net::RatPubRegisterKind::Publish
1099
+ && node_mode.reliability_mode() == Reliability::BestEffort
1100
+ {
1099
1101
  coordinator
1100
1102
  .be_publisher_vars
1101
1103
  .write()
@@ -48,15 +48,15 @@ If you have multiple Minot networks on the same physical network, use the `MINOT
48
48
 
49
49
  ## Delivery modes
50
50
 
51
- `Qos` sets a node's delivery mode. It encodes two independent things, and it is worth
52
- keeping them apart: how Zenoh carries the bytes, and what the rest of the system does
53
- when the node stops answering.
51
+ `Qos` combines `Reliability` and `Durability`. Reliability also controls failure
52
+ handling and congestion behavior.
54
53
 
55
- | | Wire | Congestion | Peer monitoring | On loss | Send dispatch |
56
- |---|---|---|---|---|---|
57
- | `Reliable` | reliable | `Block` | yes | torpedoes the run | inline, unbounded retry |
58
- | `TryReliable` | reliable | `BlockFirst` | no | rest keeps running | spawned, retry within budget |
59
- | `BestEffort` | best-effort | `Drop` | no | rest keeps running | spawned, single attempt |
54
+ | | Wire | Congestion | Peer monitoring | On loss | Send dispatch | Durability |
55
+ |---|---|---|---|---|---|---|
56
+ | `Reliable` | reliable | `Block` | yes | torpedoes the run | inline, unbounded retry | volatile |
57
+ | `TryReliable` | reliable | `BlockFirst` | no | rest keeps running | spawned, retry within budget | volatile |
58
+ | `BestEffort` | best-effort | `Drop` | no | rest keeps running | spawned, single attempt | volatile |
59
+ | `.transient_local()` | preserves base mode | preserves base mode | preserves base mode | preserves base mode | preserves base mode | replay latest sample to late subscribers |
60
60
 
61
61
  `Reliable` is the default and the strict one: peers heartbeat-monitor each other, and a
62
62
  node that stops answering tears the whole run down. That is what you want for a
@@ -70,6 +70,15 @@ publisher's loop.
70
70
 
71
71
  `BestEffort` tries once and moves on.
72
72
 
73
+ Transient-local durability combines with every reliability mode. It retains the latest
74
+ sample and replays it to late subscribers. It is disabled by default.
75
+
76
+ ```rust
77
+ let publisher = node
78
+ .create_publisher::<Transform>("/tf_static".to_owned(), Qos::Reliable.transient_local())
79
+ .await?;
80
+ ```
81
+
73
82
  Note that reliable wire delivery is close to free over TCP, which already retransmits and
74
83
  orders. The meaningful difference between `Reliable` and `TryReliable` is the failure
75
84
  policy, not the wire.
@@ -5,7 +5,9 @@ use std::{marker::PhantomData, sync::Arc};
5
5
  use mt_sea::{net::Packet, ship::NetworkShipImpl, *};
6
6
  use tokio_util::sync::CancellationToken;
7
7
 
8
- pub use mt_sea::{ArchivedMessage, NodeOptions, Qos, ReconnectPolicy, Timing};
8
+ pub use mt_sea::{
9
+ ArchivedMessage, Durability, NodeOptions, Qos, ReconnectPolicy, Reliability, Timing,
10
+ };
9
11
 
10
12
  #[derive(Debug, Clone, Copy, Default)]
11
13
  pub enum CoordMode {
@@ -217,6 +219,12 @@ impl<T: Sendable> Publisher<T> {
217
219
  }
218
220
 
219
221
  pub async fn publish(&self, data: &T) -> anyhow::Result<()> {
222
+ if self.qos.is_transient_local() {
223
+ self.ship
224
+ .get_cannon()
225
+ .retain(data, VariableType::StaticOnly, &self.topic)
226
+ .await?;
227
+ }
220
228
  self.ensure_registered().await?;
221
229
  match self.ship.ask_for_action(&self.topic).await {
222
230
  Ok((mt_sea::Action::Sail, _)) => {