Skip to content

Commit 25a1d13

Browse files
J-M-Whiteglopesdev
andauthored
Make DigitalOutput work in newer versions of SpikeGLX (#12)
* Recapitulate old DigitalOutput functionality with new SpikeGLX-CPP-SDK Update submodules to most recent commit of SpikeGLX-CPP-SDK. Alter paths in Bonsai.SpikeGLX.csproj to point to correct subdirectory in new SpikeGLX-CPP-SDK commit. Replace SetDigitalOut with SetNIDigitalOut in SpikeGLX.cs. Update DigitalOutput to use SetNIDigitalOut. * Fix location from which to copy SpikeGLX API .dll's. * Allow for use of bitmask in DigitalOutput. * Update DigitalOutput documentation to describe bitmask input option * Update DigitalOutput workflow Bonsai versions to 2.9.0 * Freeze DigitalOutput Channel property on subscription --------- Co-authored-by: glopesdev <g.lopes@neurogears.org>
1 parent 5aef9b1 commit 25a1d13

File tree

8 files changed

+96
-29
lines changed

8 files changed

+96
-29
lines changed

docs/articles/spikeglx-digitaloutput.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ uid: spikeglx-digitaloutput
33
title: DigitalOutput
44
---
55

6-
[`DigitalOutput`] writes a logical value to one or more digital output lines on a NI-DAQ device in use by SpikeGLX. Digital lines are specified in the `Channels` property as a comma separated list, (*e.g.*, `Dev6/port0/line2,Dev6/port0/line5`).
6+
[`DigitalOutput`] writes a logical value to one or more digital output lines on a NI-DAQ device through SpikeGLX. Digital lines are specified in the `Channels` property as a comma separated list, (*e.g.*, `Dev6/port0/line0:1,Dev6/port0/line5`).
77

8-
For example, the below workflow toggles a digital line between high and low every second.
8+
If an input is provided as a [`Boolean`], then all lines are set to the logical value of the input. For example, the below workflow toggles two digital lines between high and low every second.
99
:::workflow
10-
![DigitalOutput](../workflows/GettingStarted-DigitalOutput.bonsai)
10+
![DigitalOutputBoolean](../workflows/GettingStarted-DigitalOutput-Boolean.bonsai)
1111
:::
1212

13-
> [!WARNING]
14-
> Unlike `Bonsai.DAQmx`'s [`DigitalOutput`](xref:Bonsai.DAQmx.DigitalOutput), `Bonsai.SpikeGLX`'s [`DigitalOutput`] writes **the same** logical value to all of its channels.
13+
Alternatively, an input may be provided as a [`UInt32`] bitmask. In this case, each line listed in `Channels` will be set to the value of its associated bit in the bitmask. For example, the below workflow cycles through all combinations of states for two digital lines.
14+
:::workflow
15+
![DigitalOutputBitmask](../workflows/GettingStarted-DigitalOutput-Bitmask.bonsai)
16+
:::
1517

1618
<!--Reference Style Links -->
17-
[`DigitalOutput`]: xref:Bonsai.SpikeGLX.DigitalOutput
19+
[`DigitalOutput`]: xref:Bonsai.SpikeGLX.DigitalOutput
20+
[`Boolean`]: xref:System.Boolean
21+
[`UInt32`]: xref:System.UInt32

docs/docfx.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@
7979
"xref": [
8080
"https://bonsai-rx.org/docs/xrefmap.yml",
8181
"https://horizongir.github.io/reactive/xrefmap.yml",
82-
"https://bonsai-rx.org/daqmx/xrefmap.yml"
82+
"https://bonsai-rx.org/daqmx/xrefmap.yml",
83+
"https://learn.microsoft.com/en-us/dotnet/.xrefmap.json"
8384
]
8485
}
8586
}

docs/workflows/GettingStarted-DigitalOutput.bonsai renamed to docs/workflows/GettingStarted-DigitalOutput-Bitmask.bonsai

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
</Expression>
1616
<Expression xsi:type="Mod">
1717
<Operand xsi:type="WorkflowProperty" TypeArguments="sys:Int64">
18-
<Value>2</Value>
18+
<Value>4</Value>
1919
</Operand>
2020
</Expression>
2121
<Expression xsi:type="Combinator">
2222
<Combinator xsi:type="glx:DigitalOutput">
2323
<glx:Host>localhost</glx:Host>
2424
<glx:Port>4142</glx:Port>
25-
<glx:Channels>Dev6/port0/line2</glx:Channels>
25+
<glx:Channels>Dev6/port0/line0:1</glx:Channels>
2626
</Combinator>
2727
</Expression>
2828
</Nodes>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<WorkflowBuilder Version="2.9.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:rx="clr-namespace:Bonsai.Reactive;assembly=Bonsai.Core"
5+
xmlns:sys="clr-namespace:System;assembly=mscorlib"
6+
xmlns:glx="clr-namespace:Bonsai.SpikeGLX;assembly=Bonsai.SpikeGLX"
7+
xmlns="https://bonsai-rx.org/2018/workflow">
8+
<Workflow>
9+
<Nodes>
10+
<Expression xsi:type="Combinator">
11+
<Combinator xsi:type="rx:Timer">
12+
<rx:DueTime>PT0S</rx:DueTime>
13+
<rx:Period>PT1S</rx:Period>
14+
</Combinator>
15+
</Expression>
16+
<Expression xsi:type="Mod">
17+
<Operand xsi:type="WorkflowProperty" TypeArguments="sys:Int64">
18+
<Value>2</Value>
19+
</Operand>
20+
</Expression>
21+
<Expression xsi:type="Equal">
22+
<Operand xsi:type="WorkflowProperty" TypeArguments="sys:Int64">
23+
<Value>0</Value>
24+
</Operand>
25+
</Expression>
26+
<Expression xsi:type="Combinator">
27+
<Combinator xsi:type="glx:DigitalOutput">
28+
<glx:Host>localhost</glx:Host>
29+
<glx:Port>4142</glx:Port>
30+
<glx:Channels>Dev6/port0/line0:1</glx:Channels>
31+
</Combinator>
32+
</Expression>
33+
</Nodes>
34+
<Edges>
35+
<Edge From="0" To="1" Label="Source1" />
36+
<Edge From="1" To="2" Label="Source1" />
37+
<Edge From="2" To="3" Label="Source1" />
38+
</Edges>
39+
</Workflow>
40+
</WorkflowBuilder>

src/Bonsai.SpikeGLX/Bonsai.SpikeGLX.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<Content Include="..\Externals\SpikeGLX-CPP-SDK\API\*.dll" PackagePath="runtimes\win-x64\native">
10+
<Content Include="..\Externals\SpikeGLX-CPP-SDK\Windows\API\*.dll" PackagePath="runtimes\win-x64\native">
1111
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
1212
</Content>
1313
</ItemGroup>
@@ -39,7 +39,7 @@
3939
<SglxSourceFileName>C_Sglx.cs</SglxSourceFileName>
4040
</PropertyGroup>
4141
<ItemGroup>
42-
<SglxSourceFiles Include="..\Externals\SpikeGLX-CPP-SDK\C#\$(SglxSourceFileName)" />
42+
<SglxSourceFiles Include="..\Externals\SpikeGLX-CPP-SDK\Windows\C#\$(SglxSourceFileName)" />
4343
<Compile Include="$(SglxSourceFileName)" Condition="!Exists($(SglxSourceFileName))" />
4444
</ItemGroup>
4545
<Copy SourceFiles="@(SglxSourceFiles)" DestinationFolder="$(MSBuildThisFileDirectory)" />

src/Bonsai.SpikeGLX/DigitalOutput.cs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
namespace Bonsai.SpikeGLX
66
{
77
/// <summary>
8-
/// Represents an operator that sets one or more digital output lines through SpikeGLX from
8+
/// Represents an operator that sets one or more NI digital output lines through SpikeGLX from
99
/// a sequence of values representing the state of the line.
1010
/// </summary>
1111
[Combinator]
1212
[WorkflowElementCategory(ElementCategory.Sink)]
13-
[Description("Sets one or more digital output lines through SpikeGLX from a sequence of values representing the state of the line.")]
13+
[Description("Sets one or more NI digital output lines through SpikeGLX from a sequence of values representing the state of the line.")]
1414
public class DigitalOutput
1515
{
1616
/// <summary>
@@ -31,46 +31,67 @@ public class DigitalOutput
3131
/// Gets or sets the name of the channels to output the digital signal to.
3232
/// </summary>
3333
/// <remarks>
34-
/// Strings have format \"Dev6/port0/line2,Dev6/port0/line5\".
34+
/// Only lines from a single device should be listed. Valid formats include:
35+
/// <list type="bullet">
36+
/// <item>\"Dev6/port0/line2,Dev6/port0/line5\"</item>
37+
/// <item>\"Dev6/port1/line0:3\"</item>
38+
/// <item>\"Dev6/port1:2\"</item>
39+
/// </list>
3540
/// </remarks>
3641
[Description("The name of the channels to output the digital signal to.")]
3742
public string Channels { get; set; }
3843

3944
/// <summary>
40-
/// Sets one or more digital output lines through SpikeGLX from an observable sequence
41-
/// of integer values representing a digital state.
45+
/// Sets one or more NI digital output lines through SpikeGLX from an observable sequence
46+
/// of unsigned integer values representing a digital state.
4247
/// </summary>
4348
/// <param name="source">
44-
/// A sequence of 32-bit signed integers, where each value represents a high (1) or low (0)
45-
/// state to which to set the digital output line.
49+
/// A sequence of 32-bit unsigned integers, where each value represents a bitmask of
50+
/// states to which to set the digital output lines.
4651
/// </param>
4752
/// <returns>
4853
/// An observable sequence that is identical to the <paramref name="source"/> sequence
4954
/// but where there is an additional side effect of setting digital output lines through
5055
/// SpikeGLX.
5156
/// </returns>
52-
public IObservable<int> Process(IObservable<int> source)
57+
/// <remarks>
58+
/// The lowest 8 bits of the bitmask map to port0, the next higher 8 bits to port1, etc.
59+
/// This mapping is fixed, irrespective of whether only a subset of lines have been
60+
/// selected in <see cref="Channels"/>. The effect of selecting a subset of lines
61+
/// in <see cref="Channels"/> is to ignore those bits in the bitmask corresponding
62+
/// to unlisted lines.
63+
/// </remarks>
64+
public IObservable<uint> Process(IObservable<uint> source)
5365
{
5466
return Observable.Using(() => new SpikeGLX(Host, Port),
55-
connection => source.Do(input => connection.SetDigitalOut(input, Channels)));
67+
connection =>
68+
{
69+
var channels = Channels;
70+
return source.Do(input => connection.SetNIDigitalOut(channels, input));
71+
});
5672
}
5773

5874
/// <summary>
59-
/// Sets one or more digital output lines through SpikeGLX from an observable sequence
75+
/// Sets one or more NI digital output lines through SpikeGLX from an observable sequence
6076
/// of boolean values representing a digital state.
6177
/// </summary>
6278
/// <param name="source">
6379
/// A sequence of boolean values, where each value represents a high (<see langword="true"/>)
64-
/// or low (<see langword="false"/>) state to which to set the digital output line.
80+
/// or low (<see langword="false"/>) state to which to set the digital output lines.
6581
/// </param>
6682
/// <returns>
6783
/// An observable sequence that is identical to the <paramref name="source"/> sequence
6884
/// but where there is an additional side effect of setting digital output lines through
6985
/// SpikeGLX.
7086
/// </returns>
71-
public IObservable<int> Process(IObservable<bool> source)
87+
public IObservable<bool> Process(IObservable<bool> source)
7288
{
73-
return Process(source.Select(input => input ? 1 : 0));
89+
return Observable.Using(() => new SpikeGLX(Host, Port),
90+
connection =>
91+
{
92+
var channels = Channels;
93+
return source.Do(input => connection.SetNIDigitalOut(channels, input ? uint.MaxValue : uint.MinValue));
94+
});
7495
}
7596
}
7697
}

src/Bonsai.SpikeGLX/SpikeGLX.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,17 +247,18 @@ protected ulong FetchLatest(out Mat data, int js, int ip, int maxSamples, int[]
247247
}
248248

249249
/// <summary>
250-
/// Set a digital output through SpikeGLX.
250+
/// Set one or more NI digital lines through SpikeGLX.
251251
/// </summary>
252-
/// <param name="output">The digital output value to set</param>
253252
/// <param name="channels">
254253
/// The output channels to set the value of. Channel strings have form:
255254
/// "Dev6/port0/line2,Dev6/port0/line5".
256255
/// </param>
256+
/// <param name="bits">The bit mask of values to set. Each bit maps to a line.
257+
/// </param>
257258
/// <exception cref="SpikeGLXException"></exception>
258-
public void SetDigitalOut(int output, string channels)
259+
public void SetNIDigitalOut(string channels, uint bits)
259260
{
260-
int ok = C_Sglx.c_sglx_setDigitalOut(hSglx, output, channels);
261+
int ok = C_Sglx.c_sglx_ni_DO_set(hSglx, channels, bits);
261262
if (ok == 0) throw new SpikeGLXException(hSglx);
262263
}
263264

src/Externals/SpikeGLX-CPP-SDK

Submodule SpikeGLX-CPP-SDK updated 108 files

0 commit comments

Comments
 (0)