Skip to content
66 changes: 66 additions & 0 deletions pycbsdk/examples/repro_disable_others.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Repro for the bulk-setter `disable_others=True` hang report.

Mirrors the call sequence reported as hanging indefinitely in
ezmsg-blackrock:

session.set_sample_group(
2, ChannelType.FRONTEND, SampleRate.SR_30kHz, disable_others=True
)

Each phase is timed and logged so a hang shows up as the last printed
line. Run with:

python repro_disable_others.py NPLAY
python repro_disable_others.py HUB2
python repro_disable_others.py HUB1
"""

import asyncio
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))

from pycbsdk import ChannelType, DeviceType, SampleRate, Session


def _stamp(t0: float, label: str) -> None:
print(f" +{time.monotonic() - t0:6.3f}s {label}", flush=True)


async def main(device_type: str) -> None:
print(f"Connecting to {device_type}...", flush=True)
t0 = time.monotonic()

with Session(device_type=device_type) as session:
_stamp(t0, "session created")

await session.wait_until_running(timeout=10.0)
_stamp(t0, f"runlevel reached RUNNING ({session.runlevel})")

n_fe = session.num_fe_chans()
_stamp(t0, f"num_fe_chans={n_fe}")

# The exact call reported as hanging.
session.set_sample_group(
2, ChannelType.FRONTEND, SampleRate.SR_30kHz, disable_others=True
)
_stamp(t0, "set_sample_group returned")

session.sync()
_stamp(t0, "sync returned")

ch_30k = session.get_group_channels(int(SampleRate.SR_30kHz))
_stamp(t0, f"30kHz group: {sorted(ch_30k)}")

# Sanity: only chans 1, 2 should be at 30kHz.
assert sorted(ch_30k) == [1, 2], (
f"Expected [1, 2] at 30kHz, got {sorted(ch_30k)}"
)
print("OK", flush=True)


if __name__ == "__main__":
device_type = sys.argv[1] if len(sys.argv) > 1 else "NPLAY"
asyncio.run(main(device_type))
50 changes: 31 additions & 19 deletions pycbsdk/src/pycbsdk/_cdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
size_t n_channels, const uint64_t* timestamps,
void* user_data);
typedef void (*cbsdk_config_callback_fn)(const cbPKT_GENERIC* pkt, void* user_data);
typedef void (*cbsdk_runlevel_callback_fn)(uint32_t runlevel, void* user_data);
typedef void (*cbsdk_error_callback_fn)(const char* error_message, void* user_data);

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -178,6 +179,8 @@
cbsdk_callback_handle_t cbsdk_session_register_config_callback(
cbsdk_session_t session, uint16_t packet_type,
cbsdk_config_callback_fn callback, void* user_data);
cbsdk_callback_handle_t cbsdk_session_register_runlevel_callback(
cbsdk_session_t session, cbsdk_runlevel_callback_fn callback, void* user_data);
void cbsdk_session_unregister_callback(cbsdk_session_t session,
cbsdk_callback_handle_t handle);

Expand All @@ -204,13 +207,17 @@
cbsdk_result_t cbsdk_session_get_group_list(cbsdk_session_t session,
uint32_t group_id, uint16_t* list, uint32_t* count);

// Channel configuration
// Channel configuration.
// `chans` is an optional explicit list of 1-based channel ids. Pass NULL
// for the legacy "first n_chans matching" / "all matching" (n_chans=UINT32_MAX)
// behavior; pass a non-NULL pointer for the explicit-list mode.
cbsdk_result_t cbsdk_session_set_sample_group(
cbsdk_session_t session, size_t n_chans, cbproto_channel_type_t chan_type,
cbproto_group_rate_t rate, _Bool disable_others);
cbsdk_session_t session, uint32_t n_chans, const uint32_t* chans,
cbproto_channel_type_t chan_type, cbproto_group_rate_t rate,
_Bool disable_others);
cbsdk_result_t cbsdk_session_set_ac_input_coupling(
cbsdk_session_t session, size_t n_chans, cbproto_channel_type_t chan_type,
_Bool enabled);
cbsdk_session_t session, uint32_t n_chans, const uint32_t* chans,
cbproto_channel_type_t chan_type, _Bool enabled);

// Per-channel getters
cbproto_channel_type_t cbsdk_session_get_channel_type(cbsdk_session_t session, uint32_t chan_id);
Expand All @@ -226,25 +233,29 @@
cbsdk_result_t cbsdk_session_get_channel_scaling(
cbsdk_session_t session, uint32_t chan_id, cbsdk_channel_scaling_t* scaling);

// Per-channel setters
// Per-channel setters.
// `auto_sync` is non-zero to run an internal sync() before the read-modify-write
// (so any prior in-flight config from this process has landed in the cache);
// set_channel_spkthrlevel is "narrow" (CHANSETSPKTHR overwrites the only field
// the firmware reads) and so does not need an auto_sync flag.
cbsdk_result_t cbsdk_session_set_channel_label(cbsdk_session_t session,
uint32_t chan_id, const char* label);
uint32_t chan_id, const char* label, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_smpgroup(cbsdk_session_t session,
uint32_t chan_id, cbproto_group_rate_t rate);
uint32_t chan_id, cbproto_group_rate_t rate, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_smpfilter(cbsdk_session_t session,
uint32_t chan_id, uint32_t filter_id);
uint32_t chan_id, uint32_t filter_id, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_spkfilter(cbsdk_session_t session,
uint32_t chan_id, uint32_t filter_id);
uint32_t chan_id, uint32_t filter_id, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_ainpopts(cbsdk_session_t session,
uint32_t chan_id, uint32_t ainpopts);
uint32_t chan_id, uint32_t ainpopts, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_lncrate(cbsdk_session_t session,
uint32_t chan_id, uint32_t lncrate);
uint32_t chan_id, uint32_t lncrate, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_spkopts(cbsdk_session_t session,
uint32_t chan_id, uint32_t spkopts);
uint32_t chan_id, uint32_t spkopts, int auto_sync);
cbsdk_result_t cbsdk_session_set_channel_spkthrlevel(cbsdk_session_t session,
uint32_t chan_id, int32_t level);
cbsdk_result_t cbsdk_session_set_channel_autothreshold(cbsdk_session_t session,
uint32_t chan_id, _Bool enabled);
uint32_t chan_id, _Bool enabled, int auto_sync);

// Channel info field selector
typedef enum {
Expand Down Expand Up @@ -317,6 +328,7 @@

// CCF configuration files
cbsdk_result_t cbsdk_session_load_channel_map(cbsdk_session_t session, const char* filepath, uint32_t start_chan, uint32_t hs_id);
cbsdk_result_t cbsdk_session_clear_channel_map(cbsdk_session_t session);
cbsdk_result_t cbsdk_session_save_ccf(cbsdk_session_t session, const char* filename);
cbsdk_result_t cbsdk_session_load_ccf(cbsdk_session_t session, const char* filename);
cbsdk_result_t cbsdk_session_load_ccf_sync(cbsdk_session_t session, const char* filename, uint32_t timeout_ms);
Expand All @@ -331,15 +343,15 @@

// Spike sorting
cbsdk_result_t cbsdk_session_set_spike_sorting(
cbsdk_session_t session, size_t n_chans, cbproto_channel_type_t chan_type,
uint32_t sort_options);
cbsdk_session_t session, uint32_t n_chans, const uint32_t* chans,
cbproto_channel_type_t chan_type, uint32_t sort_options);
cbsdk_result_t cbsdk_session_set_channel_spike_sorting(
cbsdk_session_t session, uint32_t chan_id, uint32_t sort_options);
cbsdk_session_t session, uint32_t chan_id, uint32_t sort_options, int auto_sync);

// Spike extraction (enable/disable cbAINPSPK_EXTRACT via CHANSETSPK)
cbsdk_result_t cbsdk_session_set_spike_extraction(
cbsdk_session_t session, size_t n_chans, cbproto_channel_type_t chan_type,
bool enabled);
cbsdk_session_t session, uint32_t n_chans, const uint32_t* chans,
cbproto_channel_type_t chan_type, bool enabled);

// Clock synchronization
cbsdk_result_t cbsdk_session_get_clock_offset(cbsdk_session_t session, int64_t* offset_ns);
Expand Down
Loading
Loading