rbscan 0.2.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 (98) hide show
  1. rbscan-0.2.1/.clang-format +6 -0
  2. rbscan-0.2.1/.gitattributes +2 -0
  3. rbscan-0.2.1/.gitignore +27 -0
  4. rbscan-0.2.1/.gitlab-ci.yml +503 -0
  5. rbscan-0.2.1/CHANGELOG.md +51 -0
  6. rbscan-0.2.1/CMakeLists.txt +52 -0
  7. rbscan-0.2.1/CMakePresets.json +54 -0
  8. rbscan-0.2.1/CONTRIBUTING.md +43 -0
  9. rbscan-0.2.1/LICENSE +21 -0
  10. rbscan-0.2.1/LICENSES/HDF5-LBNL.txt +61 -0
  11. rbscan-0.2.1/LICENSES/HDF5.txt +106 -0
  12. rbscan-0.2.1/LICENSES/LZ4.txt +24 -0
  13. rbscan-0.2.1/LICENSES/libdeflate.txt +22 -0
  14. rbscan-0.2.1/LICENSES/nlohmann_json.txt +21 -0
  15. rbscan-0.2.1/LICENSES/pybind11.txt +29 -0
  16. rbscan-0.2.1/LICENSES/zlib.txt +22 -0
  17. rbscan-0.2.1/PKG-INFO +351 -0
  18. rbscan-0.2.1/README.md +311 -0
  19. rbscan-0.2.1/SECURITY.md +13 -0
  20. rbscan-0.2.1/THIRD_PARTY_NOTICES.md +24 -0
  21. rbscan-0.2.1/ci/linux.sh +176 -0
  22. rbscan-0.2.1/cmake/rbscanConfig.cmake.in +10 -0
  23. rbscan-0.2.1/cmake/rbscan_deps.cmake +175 -0
  24. rbscan-0.2.1/cmake/rbscan_embed_schemas.cmake +51 -0
  25. rbscan-0.2.1/cmake/rbscan_warnings.cmake +16 -0
  26. rbscan-0.2.1/docs/format.md +270 -0
  27. rbscan-0.2.1/examples/CMakeLists.txt +6 -0
  28. rbscan-0.2.1/examples/read_benchmark.cpp +83 -0
  29. rbscan-0.2.1/examples/record_ct_scan.cpp +236 -0
  30. rbscan-0.2.1/include/raybotic/scan/export.hpp +25 -0
  31. rbscan-0.2.1/include/raybotic/scan/rbscan.hpp +19 -0
  32. rbscan-0.2.1/include/raybotic/scan/reader.hpp +113 -0
  33. rbscan-0.2.1/include/raybotic/scan/schema.hpp +141 -0
  34. rbscan-0.2.1/include/raybotic/scan/tools.hpp +187 -0
  35. rbscan-0.2.1/include/raybotic/scan/types.hpp +202 -0
  36. rbscan-0.2.1/include/raybotic/scan/writer.hpp +239 -0
  37. rbscan-0.2.1/pyproject.toml +66 -0
  38. rbscan-0.2.1/python/CMakeLists.txt +47 -0
  39. rbscan-0.2.1/python/examples/compare_readers.py +155 -0
  40. rbscan-0.2.1/python/examples/inspect_scan.py +33 -0
  41. rbscan-0.2.1/python/examples/record_ct_scan.py +183 -0
  42. rbscan-0.2.1/python/rbscan/__init__.py +151 -0
  43. rbscan-0.2.1/python/rbscan/__main__.py +101 -0
  44. rbscan-0.2.1/python/src/bindings.cpp +731 -0
  45. rbscan-0.2.1/python/tests/conftest.py +16 -0
  46. rbscan-0.2.1/python/tests/test_crash.py +89 -0
  47. rbscan-0.2.1/python/tests/test_rbscan.py +279 -0
  48. rbscan-0.2.1/python/tests/test_tools.py +132 -0
  49. rbscan-0.2.1/schemas/annotations.json +45 -0
  50. rbscan-0.2.1/schemas/camera.json +27 -0
  51. rbscan-0.2.1/schemas/ct_acquisition.json +51 -0
  52. rbscan-0.2.1/schemas/depth_camera.json +27 -0
  53. rbscan-0.2.1/schemas/ezrt_import.json +24 -0
  54. rbscan-0.2.1/schemas/force_torque.json +19 -0
  55. rbscan-0.2.1/schemas/profile_scanner.json +24 -0
  56. rbscan-0.2.1/schemas/reconstruction.json +32 -0
  57. rbscan-0.2.1/schemas/robot_arm.json +39 -0
  58. rbscan-0.2.1/schemas/sensor.json +24 -0
  59. rbscan-0.2.1/schemas/trajectory_plan.json +48 -0
  60. rbscan-0.2.1/schemas/transform.json +20 -0
  61. rbscan-0.2.1/schemas/volume.json +44 -0
  62. rbscan-0.2.1/schemas/xray_detector.json +54 -0
  63. rbscan-0.2.1/schemas/xray_source.json +43 -0
  64. rbscan-0.2.1/src/CMakeLists.txt +92 -0
  65. rbscan-0.2.1/src/compression.cpp +568 -0
  66. rbscan-0.2.1/src/compression.hpp +47 -0
  67. rbscan-0.2.1/src/convert.cpp +87 -0
  68. rbscan-0.2.1/src/convert.hpp +15 -0
  69. rbscan-0.2.1/src/geometry.cpp +476 -0
  70. rbscan-0.2.1/src/h5.cpp +636 -0
  71. rbscan-0.2.1/src/h5.hpp +91 -0
  72. rbscan-0.2.1/src/internal.cpp +135 -0
  73. rbscan-0.2.1/src/internal.hpp +46 -0
  74. rbscan-0.2.1/src/pose_math.hpp +87 -0
  75. rbscan-0.2.1/src/raw_file.cpp +82 -0
  76. rbscan-0.2.1/src/raw_file.hpp +33 -0
  77. rbscan-0.2.1/src/reader.cpp +732 -0
  78. rbscan-0.2.1/src/schema.cpp +403 -0
  79. rbscan-0.2.1/src/segmented_writer.cpp +356 -0
  80. rbscan-0.2.1/src/series_layout.cpp +210 -0
  81. rbscan-0.2.1/src/series_layout.hpp +79 -0
  82. rbscan-0.2.1/src/thread_pool.hpp +70 -0
  83. rbscan-0.2.1/src/tools.cpp +762 -0
  84. rbscan-0.2.1/src/types.cpp +93 -0
  85. rbscan-0.2.1/src/vds.cpp +177 -0
  86. rbscan-0.2.1/src/writer.cpp +502 -0
  87. rbscan-0.2.1/src/writer_impl.hpp +129 -0
  88. rbscan-0.2.1/tests/CMakeLists.txt +26 -0
  89. rbscan-0.2.1/tests/consumer/CMakeLists.txt +21 -0
  90. rbscan-0.2.1/tests/consumer/main.cpp +52 -0
  91. rbscan-0.2.1/tests/scan_test.cpp +378 -0
  92. rbscan-0.2.1/tests/schema_test.cpp +84 -0
  93. rbscan-0.2.1/tests/segmented_test.cpp +344 -0
  94. rbscan-0.2.1/tests/test_util.hpp +34 -0
  95. rbscan-0.2.1/tests/tools_test.cpp +337 -0
  96. rbscan-0.2.1/tools/CMakeLists.txt +10 -0
  97. rbscan-0.2.1/tools/rbscan_cli.cpp +165 -0
  98. rbscan-0.2.1/uv.lock +634 -0
@@ -0,0 +1,6 @@
1
+ BasedOnStyle: Google
2
+ ColumnLimit: 90
3
+ IndentWidth: 4
4
+ AccessModifierOffset: -2
5
+ DerivePointerAlignment: false
6
+ PointerAlignment: Left
@@ -0,0 +1,2 @@
1
+ # Shell scripts run on Linux runners: keep LF even with core.autocrlf.
2
+ *.sh text eol=lf
@@ -0,0 +1,27 @@
1
+ # Build trees and outputs
2
+ build/
3
+ build-*/
4
+ build_*/
5
+ dist/
6
+ out/
7
+ tmp/
8
+
9
+ # HDF5 / zlib superbuild (sources, build trees, install)
10
+ .deps/
11
+
12
+ # Python
13
+ .venv/
14
+ __pycache__/
15
+ *.pyc
16
+ *.egg-info/
17
+ .pytest_cache/
18
+
19
+ # IDEs and local agent directories
20
+ .vs/
21
+ .vscode/
22
+ .idea/
23
+ .claude/
24
+ CMakeUserPresets.json
25
+
26
+ # Local viewer layout (Dear ImGui)
27
+ raybotic_viewer.ini
@@ -0,0 +1,503 @@
1
+ # ==============================================================================
2
+ # GitLab CI/CD Pipeline for rbscan (RayBotic Scan Data Format)
3
+ # ==============================================================================
4
+ # Stages:
5
+ # 1. lint - clang-format check, release tag matches the CMake version
6
+ # 2. build - CMake Release build (C++ shared library, CLI tool, tests)
7
+ # 3. test - C++ GoogleTest + Python pytest suite via CTest (JUnit report)
8
+ # 4. package - C++ SDK (DLLs/.so, headers, CMake config, licenses), a build of
9
+ # a separate project against that SDK, and Python wheels.
10
+ # Published to the GitLab Generic Package Registry
11
+ # (packages/generic/rbscan/<version>/) on the default branch
12
+ # (<version>-dev.<sha>) and on release tags (<version>).
13
+ # 5. release - GitLab release for a tag, notes from CHANGELOG.md
14
+ # 6. publish - wheels + sdist to PyPI on a tag (Trusted Publishing, no
15
+ # stored token); TestPyPI by hand (manual job)
16
+ #
17
+ # Linux jobs run in the manylinux_2_28 image, so the wheels can be repaired
18
+ # into manylinux wheels that PyPI accepts.
19
+ #
20
+ # Windows jobs run on the runner tagged $RBSCAN_WINDOWS_RUNNER_TAG, Linux jobs
21
+ # on the one tagged $RBSCAN_LINUX_RUNNER_TAG. Leave RBSCAN_LINUX_RUNNER_TAG
22
+ # empty (CI/CD variable or "Run pipeline" form) to skip the Linux jobs.
23
+ #
24
+ # Releasing: set the version in CMakeLists.txt, add it to CHANGELOG.md, merge
25
+ # to main and push the tag v<version>.
26
+ # ==============================================================================
27
+
28
+ stages:
29
+ - lint
30
+ - build
31
+ - test
32
+ - package
33
+ - release
34
+ - publish
35
+
36
+ workflow:
37
+ rules:
38
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
39
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
40
+ - if: $CI_COMMIT_TAG
41
+ - if: $CI_PIPELINE_SOURCE == "web"
42
+
43
+ variables:
44
+ RBSCAN_WINDOWS_RUNNER_TAG: "raybot1"
45
+ RBSCAN_LINUX_RUNNER_TAG: "raybot-linux"
46
+ # Keep .deps/ (prebuilt HDF5 + zlib, uv + Python toolchain) and build trees
47
+ # across jobs; tag pipelines still build from scratch.
48
+ GIT_CLEAN_FLAGS: "none"
49
+ GIT_CONFIG_COUNT: "1"
50
+ GIT_CONFIG_KEY_0: "safe.directory"
51
+ GIT_CONFIG_VALUE_0: "*"
52
+ # uv is installed into .deps/uv at exactly this version.
53
+ UV_VERSION: "0.12.5"
54
+ # Python versions for which wheels are built in the package stage (via uv)
55
+ RBSCAN_WHEEL_PYTHONS: "3.11 3.12 3.13 3.14"
56
+
57
+ .windows:
58
+ tags:
59
+ - $RBSCAN_WINDOWS_RUNNER_TAG
60
+
61
+ .linux:
62
+ image: quay.io/pypa/manylinux_2_28_x86_64
63
+ tags:
64
+ - $RBSCAN_LINUX_RUNNER_TAG
65
+ rules:
66
+ - if: $RBSCAN_LINUX_RUNNER_TAG
67
+
68
+ # ------------------------------------------------------------------
69
+ # Shared: uv (pinned) + .venv (with Python headers, numpy, pybind11, pytest, h5py, hdf5plugin)
70
+ # ------------------------------------------------------------------
71
+ # The GitLab Runner service runs as LocalSystem, where user-installed uv/Python
72
+ # are not in the PATH. uv is installed into .deps/uv inside the workspace.
73
+ .setup_python: &setup_python |
74
+ $ErrorActionPreference = "Stop"
75
+ $uvExe = "$PWD/.deps/uv/uv.exe"
76
+ $uvHave = if (Test-Path $uvExe) { (& $uvExe --version).Split(' ')[1] } else { "" }
77
+ if ($uvHave -ne $env:UV_VERSION) {
78
+ Write-Host "Installing uv $env:UV_VERSION into $PWD/.deps/uv..."
79
+ $env:UV_INSTALL_DIR = "$PWD/.deps/uv"
80
+ $env:UV_NO_MODIFY_PATH = "1"
81
+ irm "https://astral.sh/uv/$env:UV_VERSION/install.ps1" | iex
82
+ }
83
+ $env:PATH = "$PWD/.deps/uv;$env:PATH"
84
+ $env:UV_PYTHON_INSTALL_DIR = "$PWD/.deps/uv-python"
85
+ $env:UV_CACHE_DIR = "$PWD/.deps/uv-cache"
86
+
87
+ $venvPython = "$PWD/.venv/Scripts/python.exe"
88
+ if (-not (Test-Path $venvPython)) {
89
+ Write-Host "Creating .venv with Python 3.12 via uv..."
90
+ uv venv --python 3.12 .venv
91
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
92
+ }
93
+ uv pip install --python $venvPython "pybind11>=3.0" "numpy>=1.24" "pytest>=8" "h5py>=3.10" "hdf5plugin>=4.4"
94
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
95
+ $cmakePython = ($venvPython -replace '\\', '/')
96
+ Write-Host "Using Python for CMake: $cmakePython"
97
+
98
+ # Package version: the one in CMakeLists.txt; on a tag the tag must match it,
99
+ # elsewhere <version>-dev.<short sha>.
100
+ .release_version: &release_version |
101
+ $cmakeVersion = (Select-String -Path CMakeLists.txt -Pattern '^\s+VERSION ([0-9]+\.[0-9]+\.[0-9]+)' | Select-Object -First 1).Matches[0].Groups[1].Value
102
+ if ($env:CI_COMMIT_TAG) {
103
+ if (($env:CI_COMMIT_TAG -replace '^v', '') -ne $cmakeVersion) {
104
+ Write-Error "Tag $env:CI_COMMIT_TAG does not match the version $cmakeVersion in CMakeLists.txt"
105
+ exit 1
106
+ }
107
+ $version = $cmakeVersion
108
+ } else {
109
+ $version = "$cmakeVersion-dev.$env:CI_COMMIT_SHORT_SHA"
110
+ }
111
+ Write-Host "Package version: $version"
112
+
113
+ .configure_windows: &configure_windows |
114
+ cmake -B build -S . -G "Visual Studio 17 2022" -A x64 `
115
+ -UPython* `
116
+ "-DPython_EXECUTABLE=$cmakePython" `
117
+ -DRBSCAN_BUILD_TESTS=ON `
118
+ -DRBSCAN_BUILD_PYTHON=ON `
119
+ -DRBSCAN_BUILD_EXAMPLES=ON `
120
+ -DRBSCAN_BUILD_TOOLS=ON `
121
+ -DRBSCAN_WARNINGS_AS_ERRORS=ON
122
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
123
+ cmake --build build --config Release --parallel
124
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
125
+
126
+ # ------------------------------------------------------------------
127
+ # Stage: Lint & Version Check
128
+ # ------------------------------------------------------------------
129
+ lint:clang-format:
130
+ extends: .windows
131
+ stage: lint
132
+ script:
133
+ - |
134
+ $clangFormat = Get-Command clang-format -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source
135
+ if (-not $clangFormat) {
136
+ $searchPaths = @(
137
+ "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin\clang-format.exe",
138
+ "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\bin\clang-format.exe",
139
+ "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\x64\bin\clang-format.exe",
140
+ "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\bin\clang-format.exe",
141
+ "C:\Program Files\LLVM\bin\clang-format.exe"
142
+ )
143
+ foreach ($p in $searchPaths) {
144
+ if (Test-Path $p) {
145
+ $clangFormat = $p
146
+ break
147
+ }
148
+ }
149
+ }
150
+ if (-not $clangFormat) {
151
+ Write-Error "clang-format not found on the runner (install the VS 'C++ Clang tools' or LLVM)."
152
+ exit 1
153
+ }
154
+ Write-Host "Using clang-format from: $clangFormat"
155
+ & $clangFormat --version
156
+ $files = Get-ChildItem -Path src, include, tools, examples, tests, python -Include *.h, *.cpp, *.hpp -Recurse
157
+ $failedFiles = @()
158
+ foreach ($file in $files) {
159
+ $output = & $clangFormat --dry-run --Werror $file.FullName 2>&1
160
+ if ($LASTEXITCODE -ne 0) {
161
+ Write-Host "Formatting violation in: $($file.FullName)"
162
+ $failedFiles += $file.FullName
163
+ }
164
+ }
165
+ if ($failedFiles.Count -gt 0) {
166
+ Write-Error "Found $($failedFiles.Count) files with clang-format style violations."
167
+ exit 1
168
+ }
169
+ Write-Host "All C++ files are compliant with .clang-format."
170
+
171
+ lint:version:
172
+ extends: .windows
173
+ stage: lint
174
+ rules:
175
+ - if: $CI_COMMIT_TAG
176
+ script:
177
+ - *release_version
178
+ - |
179
+ $heading = "## [$version]"
180
+ if (-not (Select-String -Path CHANGELOG.md -SimpleMatch $heading -Quiet)) {
181
+ Write-Error "CHANGELOG.md has no section '$heading'"
182
+ exit 1
183
+ }
184
+
185
+ # ------------------------------------------------------------------
186
+ # Stage: Build Release Binaries
187
+ # ------------------------------------------------------------------
188
+ build:windows:
189
+ extends: .windows
190
+ stage: build
191
+ script:
192
+ - *setup_python
193
+ - |
194
+ $ErrorActionPreference = "Stop"
195
+ Stop-Process -Name "rbscan", "rbscan_tests", "rbscan_record_ct_scan", "rbscan_read_benchmark" -Force -ErrorAction SilentlyContinue
196
+ # Release tags always start from a clean build tree.
197
+ if ($env:CI_COMMIT_TAG -and (Test-Path build)) { Remove-Item -Path build -Recurse -Force }
198
+ - *configure_windows
199
+
200
+ build:linux:
201
+ extends: .linux
202
+ stage: build
203
+ needs: []
204
+ script:
205
+ - ci/linux.sh build
206
+
207
+ # ------------------------------------------------------------------
208
+ # Stage: Execute CTest Suite & Export JUnit Report
209
+ # ------------------------------------------------------------------
210
+ test:windows:
211
+ extends: .windows
212
+ stage: test
213
+ needs:
214
+ - build:windows
215
+ script:
216
+ - *setup_python
217
+ - |
218
+ $ErrorActionPreference = "Stop"
219
+ if (-not (Test-Path "build/bin/Release/rbscan.dll")) {
220
+ Write-Error "No Release build in build/ (build:windows must run on this runner first)"
221
+ exit 1
222
+ }
223
+ ctest --test-dir build -C Release --output-on-failure --output-junit ctest_report.xml
224
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
225
+ artifacts:
226
+ when: always
227
+ expire_in: 14 days
228
+ reports:
229
+ junit: build/ctest_report.xml
230
+
231
+ test:linux:
232
+ extends: .linux
233
+ stage: test
234
+ needs:
235
+ - build:linux
236
+ script:
237
+ - ci/linux.sh test
238
+ artifacts:
239
+ when: always
240
+ expire_in: 14 days
241
+ reports:
242
+ junit: build-linux/ctest_report.xml
243
+
244
+ # ------------------------------------------------------------------
245
+ # Stage: Package SDK & Python Wheels, publish to the Package Registry
246
+ # ------------------------------------------------------------------
247
+ package:windows:
248
+ extends: .windows
249
+ stage: package
250
+ needs:
251
+ - build:windows
252
+ - test:windows
253
+ script:
254
+ - *setup_python
255
+ - *release_version
256
+ - |
257
+ $ErrorActionPreference = "Stop"
258
+ if (-not (Test-Path "build/bin/Release/rbscan.dll")) {
259
+ Write-Error "No Release build in build/ (build:windows must run on this runner first)"
260
+ exit 1
261
+ }
262
+
263
+ # Clean previous packaging output (workspace persists on runner)
264
+ if (Test-Path "dist") { Remove-Item -Path "dist" -Recurse -Force }
265
+ New-Item -ItemType Directory -Path "dist/dll", "dist/wheels", "dist/sdk" -Force | Out-Null
266
+
267
+ # 1. Install the C++ SDK (rbscan.dll, rbscan_hdf5.dll, rbscan.exe, rbscan.lib,
268
+ # headers, CMake package config, embedded schemas and licenses)
269
+ cmake --install build --config Release --component sdk --prefix dist/sdk
270
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
271
+
272
+ # A separate project must find and use the installed SDK.
273
+ if (Test-Path "build/consumer") { Remove-Item -Path "build/consumer" -Recurse -Force }
274
+ cmake -S tests/consumer -B build/consumer -G "Visual Studio 17 2022" -A x64 "-DCMAKE_PREFIX_PATH=$PWD/dist/sdk"
275
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
276
+ cmake --build build/consumer --config Release
277
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
278
+ & build/consumer/Release/rbscan_consumer.exe build
279
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
280
+
281
+ # Copy the DLLs, import lib, and CLI executable to dist/dll/ for direct access
282
+ Copy-Item -Path "dist/sdk/bin/rbscan.dll" -Destination "dist/dll/" -Force
283
+ Copy-Item -Path "dist/sdk/bin/rbscan_hdf5.dll" -Destination "dist/dll/" -Force
284
+ Copy-Item -Path "dist/sdk/bin/rbscan.exe" -Destination "dist/dll/" -Force
285
+ Copy-Item -Path "dist/sdk/lib/rbscan.lib" -Destination "dist/dll/" -Force
286
+
287
+ # Also bundle the full C++ SDK into a single archive
288
+ Compress-Archive -Path "dist/sdk/*" -DestinationPath "dist/rbscan-sdk-win64.zip" -Force
289
+ Remove-Item -Path "dist/sdk" -Recurse -Force
290
+
291
+ # 2. Build Python wheels (reuses prebuilt HDF5 in .deps/)
292
+ $versions = ($env:RBSCAN_WHEEL_PYTHONS -split '\s+') | Where-Object { $_ }
293
+ foreach ($py in $versions) {
294
+ Write-Host "Building Python wheel for Python $py..."
295
+ uv build --wheel --python $py -o dist/wheels
296
+ if ($LASTEXITCODE -ne 0) {
297
+ Write-Error "Failed to build wheel for Python $py"
298
+ exit $LASTEXITCODE
299
+ }
300
+ }
301
+ Remove-Item -Path "dist/wheels/.gitignore" -Force -ErrorAction SilentlyContinue
302
+
303
+ $wheels = Get-ChildItem -Path "dist/wheels" -Filter "*.whl"
304
+ if (-not $wheels) {
305
+ Write-Error "No Python wheels were built in dist/wheels"
306
+ exit 1
307
+ }
308
+
309
+ # Smoke-test the Python 3.12 wheel in an isolated environment
310
+ $testWheel = $wheels | Where-Object { $_.Name -match '-cp312-' } | Select-Object -First 1
311
+ if ($testWheel) {
312
+ Write-Host "Smoke-testing wheel $($testWheel.Name)..."
313
+ uv run --isolated --no-project --python 3.12 --with $testWheel.FullName --with pytest --with h5py --with hdf5plugin pytest python/tests
314
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
315
+ }
316
+
317
+ Write-Host "=== Packaged Artifacts ==="
318
+ Get-ChildItem -Path "dist" -Recurse -File | ForEach-Object {
319
+ Write-Host (" {0} ({1:N1} KB)" -f $_.FullName.Substring($PWD.Path.Length + 1), ($_.Length / 1KB))
320
+ }
321
+
322
+ # 3. Publish SDK archive, DLLs, and Python wheels to the GitLab Generic Package Registry
323
+ # on the default branch and on release tags (packages/generic/rbscan/<version>/...).
324
+ if ($env:CI_COMMIT_TAG -or ($env:CI_COMMIT_BRANCH -eq $env:CI_DEFAULT_BRANCH)) {
325
+ $filesToPublish = @()
326
+ $filesToPublish += Get-Item "dist/rbscan-sdk-win64.zip"
327
+ $filesToPublish += Get-ChildItem -Path "dist/dll" -File
328
+ $filesToPublish += Get-ChildItem -Path "dist/wheels" -Filter "*.whl"
329
+
330
+ foreach ($file in $filesToPublish) {
331
+ $url = "$env:CI_API_V4_URL/projects/$env:CI_PROJECT_ID/packages/generic/rbscan/$version/$($file.Name)"
332
+ Write-Host "Publishing $($file.Name) ($([math]::Round($file.Length / 1KB, 1)) KB) to $url ..."
333
+ $status = curl.exe --fail --silent --show-error --header "JOB-TOKEN: $env:CI_JOB_TOKEN" --upload-file $file.FullName --write-out "%{http_code}" $url
334
+ if ($LASTEXITCODE -ne 0) {
335
+ Write-Error "Upload of $($file.Name) to the Generic Package Registry failed (HTTP $status, curl exit code $LASTEXITCODE)"
336
+ exit 1
337
+ }
338
+ Write-Host "Published: $url"
339
+ }
340
+ }
341
+ artifacts:
342
+ name: "rbscan-windows-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
343
+ expire_in: 30 days
344
+ paths:
345
+ - "dist/dll/rbscan.dll"
346
+ - "dist/dll/rbscan_hdf5.dll"
347
+ - "dist/dll/rbscan.lib"
348
+ - "dist/dll/rbscan.exe"
349
+ - "dist/rbscan-sdk-win64.zip"
350
+ - "dist/wheels/*.whl"
351
+
352
+ package:linux:
353
+ extends: .linux
354
+ stage: package
355
+ needs:
356
+ - build:linux
357
+ - test:linux
358
+ script:
359
+ - ci/linux.sh version
360
+ - ci/linux.sh package
361
+ - |
362
+ if [ -n "${CI_COMMIT_TAG:-}" ] || [ "${CI_COMMIT_BRANCH:-}" = "$CI_DEFAULT_BRANCH" ]; then
363
+ ci/linux.sh publish
364
+ fi
365
+ artifacts:
366
+ name: "rbscan-linux-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA"
367
+ expire_in: 30 days
368
+ paths:
369
+ - "dist/rbscan-sdk-linux-x86_64.tar.gz"
370
+ - "dist/rbscan-[0-9]*.tar.gz"
371
+ - "dist/wheels/*.whl"
372
+
373
+ # ------------------------------------------------------------------
374
+ # Stage: GitLab Release (tags only)
375
+ # ------------------------------------------------------------------
376
+ release:
377
+ extends: .windows
378
+ stage: release
379
+ rules:
380
+ - if: $CI_COMMIT_TAG
381
+ needs:
382
+ - job: package:windows
383
+ artifacts: true
384
+ - job: package:linux
385
+ artifacts: true
386
+ optional: true
387
+ script:
388
+ - *release_version
389
+ - |
390
+ $ErrorActionPreference = "Stop"
391
+ # Release notes: the CHANGELOG.md section of this version.
392
+ $notes = @()
393
+ $inSection = $false
394
+ foreach ($line in Get-Content CHANGELOG.md -Encoding UTF8) {
395
+ if ($line -like "## *") {
396
+ if ($inSection) { break }
397
+ $inSection = $line.StartsWith("## [$version]")
398
+ continue
399
+ }
400
+ if ($inSection) { $notes += $line }
401
+ }
402
+ $packageUrl = "$env:CI_API_V4_URL/projects/$env:CI_PROJECT_ID/packages/generic/rbscan/$version"
403
+ $files = @(Get-Item "dist/rbscan-sdk-*" -ErrorAction SilentlyContinue) + @(Get-ChildItem "dist/wheels" -Filter "*.whl" -ErrorAction SilentlyContinue)
404
+ $links = @($files | ForEach-Object {
405
+ @{ name = $_.Name; url = "$packageUrl/$($_.Name)"; link_type = "package" }
406
+ })
407
+ $body = @{
408
+ tag_name = $env:CI_COMMIT_TAG
409
+ name = "rbscan $version"
410
+ description = ($notes -join "`n").Trim()
411
+ assets = @{ links = $links }
412
+ } | ConvertTo-Json -Depth 5
413
+ $bodyFile = Join-Path $env:TEMP "rbscan-release.json"
414
+ [System.IO.File]::WriteAllText($bodyFile, $body, (New-Object System.Text.UTF8Encoding $false))
415
+ curl.exe --fail --silent --show-error --header "JOB-TOKEN: $env:CI_JOB_TOKEN" --header "Content-Type: application/json" --data "@$bodyFile" "$env:CI_API_V4_URL/projects/$env:CI_PROJECT_ID/releases"
416
+ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
417
+ Write-Host "`nCreated release rbscan $version with $($links.Count) package links"
418
+
419
+ # ------------------------------------------------------------------
420
+ # Stage: Publish to PyPI / TestPyPI (Trusted Publishing)
421
+ # ------------------------------------------------------------------
422
+ # PyPI trusts this project's GitLab OIDC token (pending/trusted publisher:
423
+ # namespace raybotic/machine, project rbscan, workflow .gitlab-ci.yml,
424
+ # environment pypi resp. testpypi). The job swaps the token for a short-lived
425
+ # upload token; nothing is stored in CI/CD variables. PyPI accepts every
426
+ # version only once.
427
+ .pypi_upload:
428
+ extends: .linux
429
+ image: python:3.12-slim
430
+ variables:
431
+ # Before the artifacts arrive: drop old dist/ files, keep the build caches.
432
+ GIT_CLEAN_FLAGS: "-ffdx -e .deps -e .venv -e build-linux"
433
+ needs:
434
+ - job: package:windows
435
+ artifacts: true
436
+ - job: package:linux
437
+ artifacts: true
438
+ script:
439
+ - python -m pip install --quiet --upgrade twine
440
+ - ls -l dist dist/wheels
441
+ - python -m twine check --strict dist/wheels/*.whl dist/rbscan-[0-9]*.tar.gz
442
+ - |
443
+ api_token=$(python - <<'PY'
444
+ import json, os, urllib.request
445
+ req = urllib.request.Request(
446
+ os.environ["PYPI_MINT_URL"],
447
+ data=json.dumps({"token": os.environ["PYPI_ID_TOKEN"]}).encode(),
448
+ headers={"Content-Type": "application/json"},
449
+ method="POST",
450
+ )
451
+ try:
452
+ with urllib.request.urlopen(req) as r:
453
+ print(json.load(r)["token"])
454
+ except urllib.error.HTTPError as e:
455
+ import base64, sys
456
+ print(f"minting the upload token failed: {e.code} {e.read().decode()}", file=sys.stderr)
457
+ # The claims PyPI matches against the trusted publisher (not secret;
458
+ # the token itself is never printed).
459
+ payload = os.environ["PYPI_ID_TOKEN"].split(".")[1]
460
+ claims = json.loads(base64.urlsafe_b64decode(payload + "=" * (-len(payload) % 4)))
461
+ print("This job's OIDC claims; the trusted publisher on PyPI must match them:", file=sys.stderr)
462
+ for key in ("aud", "namespace_path", "project_path", "ci_config_ref_uri",
463
+ "environment", "ref_path", "ref_protected"):
464
+ print(f" {key}: {claims.get(key)}", file=sys.stderr)
465
+ raise SystemExit(1)
466
+ PY
467
+ ) || exit 1
468
+ - python -m twine upload --non-interactive --repository-url "$PYPI_UPLOAD_URL" $TWINE_EXTRA -u __token__ -p "$api_token" dist/wheels/*.whl dist/rbscan-[0-9]*.tar.gz
469
+
470
+ publish:testpypi:
471
+ extends: .pypi_upload
472
+ stage: publish
473
+ environment: testpypi
474
+ id_tokens:
475
+ PYPI_ID_TOKEN:
476
+ aud: testpypi
477
+ variables:
478
+ PYPI_MINT_URL: "https://test.pypi.org/_/oidc/mint-token"
479
+ PYPI_UPLOAD_URL: "https://test.pypi.org/legacy/"
480
+ # A trial run may repeat a version that is already there.
481
+ TWINE_EXTRA: "--skip-existing"
482
+ rules:
483
+ - if: $RBSCAN_LINUX_RUNNER_TAG == null || $RBSCAN_LINUX_RUNNER_TAG == ""
484
+ when: never
485
+ - if: $CI_COMMIT_TAG || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
486
+ when: manual
487
+ allow_failure: true
488
+
489
+ publish:pypi:
490
+ extends: .pypi_upload
491
+ stage: publish
492
+ environment: pypi
493
+ id_tokens:
494
+ PYPI_ID_TOKEN:
495
+ aud: pypi
496
+ variables:
497
+ PYPI_MINT_URL: "https://pypi.org/_/oidc/mint-token"
498
+ PYPI_UPLOAD_URL: "https://upload.pypi.org/legacy/"
499
+ TWINE_EXTRA: ""
500
+ rules:
501
+ - if: $RBSCAN_LINUX_RUNNER_TAG == null || $RBSCAN_LINUX_RUNNER_TAG == ""
502
+ when: never
503
+ - if: $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+$/
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to rbscan are listed here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the library uses
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ The library version (below) and the on-disk **format version** (`format_version`
8
+ in every scan, see [docs/format.md](docs/format.md)) are independent: a
9
+ library release can read every format version with the same major number
10
+ (`1.x`). Schemas carry their own version (`raybotic.xray_detector/1`).
11
+
12
+ ## [Unreleased]
13
+
14
+ ## [0.2.1] - 2026-11-02
15
+
16
+ First release published on PyPI and GitHub. Writes and reads format version 1.0.
17
+
18
+ ### Fixed
19
+
20
+ - The Linux release build no longer stops without a message on a fresh CI
21
+ workspace (the reason 0.2.0 was never published).
22
+
23
+ ### Added
24
+
25
+ - Scan directories with an HDF5 master file (`scan.rbscan.h5`) and segmented
26
+ sensor streams (`seg_NNNNNN.h5`), linked into the master as virtual datasets.
27
+ - Inline and segmented series, multi-process recording, live reading via SWMR,
28
+ appending to stored inline series and `refresh()` while reading.
29
+ - Compression: Bitshuffle+LZ4 (default, HDF5 filter 32008, readable with
30
+ `hdf5plugin`), shuffle+deflate, deflate, none; parallel encoding and decoding
31
+ outside the global HDF5 lock.
32
+ - Built-in schemas for X-ray source and detector, cameras, depth cameras,
33
+ profile scanners, force/torque sensors, robot arms, transforms, CT
34
+ acquisitions, volumes, annotations, reconstruction settings, trajectory plans
35
+ and EZRT imports; custom schemas at runtime or via `RBSCAN_SCHEMA_PATH`.
36
+ - Tools in C++, Python and the `rbscan` CLI: `info`, `validate`,
37
+ `resolve-geometry`, `rebuild`, `schemas`.
38
+ - `rebuild` recovers a scan whose recording process crashed: it clears the
39
+ HDF5 status flags the crashed writer left in the master file and relinks
40
+ every segment on disk.
41
+ - C++ SDK (Windows, Linux) with CMake package config.
42
+ - On PyPI (`pip install rbscan`): wheels for CPython 3.11–3.14 on Windows x64
43
+ and Linux x86_64 (`manylinux_2_28`), plus the source distribution.
44
+ - `Array::values<T>()` returns a `std::span` on a named array and an owning
45
+ `std::vector<T>` on a temporary (`reader.read(...).values<T>()`), so the
46
+ result never dangles.
47
+
48
+ ## [0.2.0] - 2026-11-02
49
+
50
+ Tagged but not published: its Linux release build failed. Everything is in
51
+ 0.2.1.
@@ -0,0 +1,52 @@
1
+ cmake_minimum_required(VERSION 3.24)
2
+
3
+ project(rbscan
4
+ VERSION 0.2.1
5
+ DESCRIPTION "RayBotic scan data format: HDF5 master file + segmented sensor streams"
6
+ LANGUAGES C CXX)
7
+
8
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
9
+
10
+ set(CMAKE_CXX_STANDARD 20)
11
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
12
+ set(CMAKE_CXX_EXTENSIONS OFF)
13
+
14
+ if(PROJECT_IS_TOP_LEVEL)
15
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
16
+ endif()
17
+
18
+ option(RBSCAN_BUILD_TESTS "Build the C++ unit tests" ${PROJECT_IS_TOP_LEVEL})
19
+ option(RBSCAN_BUILD_PYTHON "Build the Python extension module" ${PROJECT_IS_TOP_LEVEL})
20
+ option(RBSCAN_BUILD_EXAMPLES "Build the C++ examples" ${PROJECT_IS_TOP_LEVEL})
21
+ option(RBSCAN_BUILD_TOOLS "Build the rbscan command line tool" ON)
22
+ option(RBSCAN_INSTALL "Generate install rules (off when used as a subproject)" ${PROJECT_IS_TOP_LEVEL})
23
+ option(RBSCAN_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
24
+
25
+ # Everything lands next to each other, so rbscan.dll / hdf5.dll are found by
26
+ # tests and examples without touching PATH.
27
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
28
+ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
29
+ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
30
+
31
+ include(rbscan_warnings)
32
+ include(rbscan_deps)
33
+ include(rbscan_embed_schemas)
34
+
35
+ add_subdirectory(src)
36
+
37
+ if(RBSCAN_BUILD_TOOLS)
38
+ add_subdirectory(tools)
39
+ endif()
40
+
41
+ if(RBSCAN_BUILD_PYTHON)
42
+ add_subdirectory(python)
43
+ endif()
44
+
45
+ if(RBSCAN_BUILD_EXAMPLES)
46
+ add_subdirectory(examples)
47
+ endif()
48
+
49
+ if(RBSCAN_BUILD_TESTS)
50
+ enable_testing()
51
+ add_subdirectory(tests)
52
+ endif()