Skip to content

Commit aa9fa3b

Browse files
committed
Allow single PolledBno055Data operator per device name
- Use static HashSet to hold names of DeviceNames that have been used to create a Subscription to upstream sequence. - Explictly remove DeviceNames from HashSet by modifying the IDisposable returned by Subscribe that is called at sequence end.
1 parent 489249a commit aa9fa3b

File tree

1 file changed

+42
-3
lines changed

1 file changed

+42
-3
lines changed

OpenEphys.Onix1/PolledBno055Data.cs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.ComponentModel;
34
using System.Linq;
45
using System.Reactive.Concurrency;
6+
using System.Reactive.Disposables;
57
using System.Reactive.Linq;
68
using Bonsai;
79

@@ -18,7 +20,21 @@ namespace OpenEphys.Onix1
1820
[Description("Polls a Bno055 9-axis IMU to produce a sequence Bno055 data frames.")]
1921
public class PolledBno055Data : Source<Bno055DataFrame>
2022
{
21-
/// <inheritdoc cref = "SingleDeviceFactory.DeviceName"/>
23+
static readonly HashSet<string> registeredValues = new();
24+
static readonly object registrationLock = new();
25+
26+
/// <summary>
27+
/// Gets or sets a unique device name. Only a single <see cref="PolledBno055Data"/> operator can be
28+
/// used per distinct device name.
29+
/// </summary>
30+
/// <remarks>
31+
/// The device name provides a unique, human-readable identifier that is used to link software
32+
/// elements for configuration, control, and data streaming to hardware. For instance, it can be used
33+
/// to link configuration operators to data IO operators within a workflow. This value is
34+
/// usually not set manually, but is assigned in a <see cref="MultiDeviceFactory"/> to correspond to a
35+
/// fixed address with a piece of hardware such as a headstage. This address is used for software
36+
/// communication.
37+
/// </remarks>
2238
[TypeConverter(typeof(PolledBno055.NameConverter))]
2339
[Description(SingleDeviceFactory.DeviceNameDescription)]
2440
[Category(DeviceFactory.ConfigurationCategory)]
@@ -73,8 +89,10 @@ public override IObservable<Bno055DataFrame> Generate()
7389
/// <returns>A sequence of <see cref="Bno055DataFrame">Bno055DataFrames</see>.</returns>
7490
public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource> source)
7591
{
92+
var deviceName = DeviceName;
7693
var polled = PolledRegisters;
77-
return DeviceManager.GetDevice(DeviceName).SelectMany(
94+
95+
return DeviceManager.GetDevice(deviceName).SelectMany(
7896
deviceInfo =>
7997
{
8098
return !((PolledBno055DeviceInfo)deviceInfo).Enable
@@ -85,7 +103,19 @@ public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource
85103
var passthrough = device.GetPassthroughDeviceContext(typeof(DS90UB9x));
86104
var i2c = new I2CRegisterContext(passthrough, PolledBno055.BNO055Address);
87105

88-
return source.SubscribeSafe(observer, _ =>
106+
// NB: Only allow on PolledBno055Data operator per unique DeviceName
107+
lock (registrationLock)
108+
{
109+
if (registeredValues.Contains(deviceName))
110+
{
111+
throw new InvalidOperationException($"There is already a {nameof(PolledBno055Data)} linked to '{deviceName}'. " +
112+
$"Only one {nameof(PolledBno055Data)} operator is allowed for each distinct {nameof(DeviceName)}.");
113+
}
114+
115+
registeredValues.Add(deviceName);
116+
}
117+
118+
var s = source.SubscribeSafe(observer, _ =>
89119
{
90120
Bno055DataFrame frame = default;
91121
device.Context.EnsureContext(() =>
@@ -139,6 +169,15 @@ public unsafe IObservable<Bno055DataFrame> Generate<TSource>(IObservable<TSource
139169
observer.OnNext(frame);
140170
}
141171
});
172+
173+
return Disposable.Create(() =>
174+
{
175+
lock (registrationLock)
176+
{
177+
registeredValues.Remove(deviceName);
178+
}
179+
s.Dispose();
180+
});
142181
});
143182
});
144183
}

0 commit comments

Comments
 (0)