Skip to content

Commit

Permalink
Add spinnaker to drivers (#88)
Browse files Browse the repository at this point in the history
This adds v0.1.1 of the spinnaker acquire driver to the drivers list
used to build the Python package. It also adds some very basic Python
tests to check that the devices are available.

---------

Co-authored-by: Nathan Clack <[email protected]>
  • Loading branch information
andy-sweet and nclack authored Oct 2, 2023
1 parent cb2dd9b commit 13c0dfb
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 41 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/test_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,55 @@ jobs:
run: |
python -m pytest -k test_egrabber --color=yes --cov-report=xml --cov=acquire --maxfail=5 --log-cli-level=0
spinnaker:
name: Python ${{ matrix.python }} (Spinnaker)
runs-on:
- self-hosted
- spinnaker
- BFLY-U3-23S6M-C
- ORX-10GS-51S5M-C
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
python: [ "3.8", "3.9", "3.10" ]

permissions:
actions: write
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Cancel Previous Runs
uses: styfle/[email protected]
with:
access_token: ${{ github.token }}

- uses: actions/checkout@v3
with:
submodules: true
ref: ${{ github.event.pull_request.head.sha }}

- name: Get CMake 3.24
uses: lukka/get-cmake@latest
with:
cmakeVersion: 3.24.3

- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}

- name: Install
run: |
pip install --upgrade pip
pip install -e .[testing]
- name: Test
run: |
python -m pytest -k test_spinnaker --color=yes --cov-report=xml --cov=acquire --maxfail=5 --log-cli-level=0
typing:
name: mypy typing
runs-on: windows-latest # FIXME (aliddell): stubtest claims to fail to find shared libs on Linux
Expand Down
3 changes: 3 additions & 0 deletions acquire-libs/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
cmake_minimum_required(VERSION 3.5)
project(acquire-libs)

function(aq_require tgt)
if(NOT TARGET ${tgt})
add_subdirectory(${tgt})
Expand Down
36 changes: 23 additions & 13 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fs;
use serde::Deserialize;
use std::fs;

/// Struct representation of the manifest file drivers.json
#[derive(Deserialize)]
Expand All @@ -9,6 +9,7 @@ struct DriverManifest {
acquire_driver_zarr: String,
acquire_driver_egrabber: String,
acquire_driver_hdcam: String,
acquire_driver_spinnaker: String,
}

fn main() {
Expand All @@ -22,10 +23,10 @@ fn main() {
.define("CMAKE_OSX_DEPLOYMENT_TARGET", "10.15")
.build();

let drivers_json = fs::read_to_string("drivers.json")
.expect("Failed to read from drivers.json.");
let tags: DriverManifest = serde_json::from_str(drivers_json.as_str())
.expect("Failed to parse drivers.json");
let drivers_json =
fs::read_to_string("drivers.json").expect("Failed to read from drivers.json.");
let tags: DriverManifest =
serde_json::from_str(drivers_json.as_str()).expect("Failed to parse drivers.json");

let drivers_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("drivers");

Expand All @@ -49,6 +50,11 @@ fn main() {
"acquire-driver-hdcam",
tags.acquire_driver_hdcam.as_str(),
);
fetch_acquire_driver(
&drivers_dir,
"acquire-driver-spinnaker",
tags.acquire_driver_spinnaker.as_str(),
);

println!("cargo:rustc-link-search=native={}/lib", dst.display());
println!("cargo:rustc-link-lib=static=acquire-video-runtime");
Expand Down Expand Up @@ -105,11 +111,15 @@ fn fetch_acquire_driver(dst: &std::path::PathBuf, name: &str, tag: &str) {
let archive = match request.send() {
Ok(r) => r.bytes(),
Err(err) => panic!("HTTP request for {} failed, got {}", &name, err),
}.expect(&*format!("Failed to get response body for {} as bytes.", name));
}
.expect(&*format!(
"Failed to get response body for {} as bytes.",
name
));

zip_extract::extract(
std::io::Cursor::new(archive), &dst, true,
).expect(&*format!("Failed to extract {name}-{tag}-{build}.zip from response."));
zip_extract::extract(std::io::Cursor::new(archive), &dst, true).expect(&*format!(
"Failed to extract {name}-{tag}-{build}.zip from response."
));

copy_acquire_driver(&dst, name);
}
Expand All @@ -131,8 +141,8 @@ fn copy_acquire_driver(dst: &std::path::PathBuf, name: &str) {
format!("{}/lib/{lib}", dst.display()),
format!("python/acquire/{lib}"),
)
.expect(&format!(
"Failed to copy {}/lib/{lib} to python folder.",
dst.display()
));
.expect(&format!(
"Failed to copy {}/lib/{lib} to python folder.",
dst.display()
));
}
5 changes: 3 additions & 2 deletions drivers.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"acquire-driver-common": "0.1.5",
"acquire-driver-zarr": "0.1.4",
"acquire-driver-egrabber": "0.1.4",
"acquire-driver-hdcam": "0.1.6"
}
"acquire-driver-hdcam": "0.1.6",
"acquire-driver-spinnaker": "0.1.1"
}
58 changes: 33 additions & 25 deletions src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ impl_plain_old_dict!(ChunkingProperties);

impl Default for ChunkingProperties {
fn default() -> Self {
let tile = Python::with_gil(|py| {
Py::new(py, TileShape::default()).unwrap()
});
let tile = Python::with_gil(|py| Py::new(py, TileShape::default()).unwrap());
Self {
max_bytes_per_chunk: Default::default(),
tile,
Expand Down Expand Up @@ -80,9 +78,7 @@ impl_plain_old_dict!(StorageProperties);

impl Default for StorageProperties {
fn default() -> Self {
let chunking = Python::with_gil(|py| {
Py::new(py, ChunkingProperties::default()).unwrap()
});
let chunking = Python::with_gil(|py| Py::new(py, ChunkingProperties::default()).unwrap());
Self {
filename: Default::default(),
external_metadata_json: Default::default(),
Expand Down Expand Up @@ -118,15 +114,23 @@ impl TryFrom<capi::StorageProperties> for StorageProperties {
};

let chunking = Python::with_gil(|py| {
let tile = Py::new(py, TileShape {
width: value.chunking.tile.width,
height: value.chunking.tile.height,
planes: value.chunking.tile.planes,
}).unwrap();
Py::new(py, ChunkingProperties {
max_bytes_per_chunk: value.chunking.max_bytes_per_chunk,
tile,
}).unwrap()
let tile = Py::new(
py,
TileShape {
width: value.chunking.tile.width,
height: value.chunking.tile.height,
planes: value.chunking.tile.planes,
},
)
.unwrap();
Py::new(
py,
ChunkingProperties {
max_bytes_per_chunk: value.chunking.max_bytes_per_chunk,
tile,
},
)
.unwrap()
});

Ok(Self {
Expand Down Expand Up @@ -196,18 +200,18 @@ impl TryFrom<&StorageProperties> for capi::StorageProperties {
} {
Err(anyhow::anyhow!("Failed acquire api status check"))
} else if !unsafe {
capi::storage_properties_set_chunking_props(&mut out,
tile_shape.width,
tile_shape.height,
tile_shape.planes,
chunking_props.max_bytes_per_chunk,
capi::storage_properties_set_chunking_props(
&mut out,
tile_shape.width,
tile_shape.height,
tile_shape.planes,
chunking_props.max_bytes_per_chunk,
) == 1
} {
Err(anyhow::anyhow!("Failed acquire api status check"))
} else if !unsafe {
capi::storage_properties_set_enable_multiscale(&mut out,
value.enable_multiscale as u8,
) == 1
capi::storage_properties_set_enable_multiscale(&mut out, value.enable_multiscale as u8)
== 1
} {
Err(anyhow::anyhow!("Failed acquire api status check"))
} else {
Expand Down Expand Up @@ -248,7 +252,9 @@ impl Default for capi::PixelScale {
}
}

impl Default for capi::StorageProperties_storage_properties_chunking_s_storage_properties_chunking_tile_s {
impl Default
for capi::StorageProperties_storage_properties_chunking_s_storage_properties_chunking_tile_s
{
fn default() -> Self {
Self {
width: Default::default(),
Expand All @@ -258,7 +264,9 @@ impl Default for capi::StorageProperties_storage_properties_chunking_s_storage_p
}
}

impl TryFrom<&TileShape> for capi::StorageProperties_storage_properties_chunking_s_storage_properties_chunking_tile_s {
impl TryFrom<&TileShape>
for capi::StorageProperties_storage_properties_chunking_s_storage_properties_chunking_tile_s
{
type Error = anyhow::Error;

fn try_from(value: &TileShape) -> Result<Self, Self::Error> {
Expand Down
2 changes: 1 addition & 1 deletion tests/test_egrabber.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def runtime(_runtime: acquire.Runtime):
_runtime.set_configuration(acquire.Properties())


def test_vieworks_camera_is_preset(runtime: acquire.Runtime):
def test_vieworks_camera_is_present(runtime: acquire.Runtime):
dm = runtime.device_manager()
assert dm.select(DeviceKind.Camera, "VIEWORKS.*")

Expand Down
25 changes: 25 additions & 0 deletions tests/test_spinnaker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import acquire
import pytest
from acquire import DeviceKind


@pytest.fixture(scope="module")
def _runtime():
runtime = acquire.Runtime()
yield runtime


@pytest.fixture(scope="function")
def runtime(_runtime: acquire.Runtime):
yield _runtime
_runtime.set_configuration(acquire.Properties())


def test_blackfly_camera_is_present(runtime: acquire.Runtime):
dm = runtime.device_manager()
assert dm.select(DeviceKind.Camera, ".*BFLY-U3-23S6M.*")


def test_oryx_camera_is_present(runtime: acquire.Runtime):
dm = runtime.device_manager()
assert dm.select(DeviceKind.Camera, ".*ORX-10GS-51S5M.*")

0 comments on commit 13c0dfb

Please sign in to comment.