Skip to content

Commit

Permalink
Merge pull request #214 from open-ephys/issue-88
Browse files Browse the repository at this point in the history
Add configuration GUI for NeuropixelsV2e
jonnew authored Aug 20, 2024
2 parents 5ed0c9e + 118a7ab commit 60e22f1
Showing 41 changed files with 13,674 additions and 175 deletions.
84 changes: 42 additions & 42 deletions .bonsai/Bonsai.config
Original file line number Diff line number Diff line change
@@ -50,49 +50,49 @@
<AssemblyReference assemblyName="Bonsai.Vision.Design" />
</AssemblyReferences>
<AssemblyLocations>
<AssemblyLocation assemblyName="Bonsai" processorArchitecture="MSIL" location="Packages\Bonsai.2.8.5\lib\net48\Bonsai.exe" />
<AssemblyLocation assemblyName="Bonsai.Core" processorArchitecture="MSIL" location="Packages\Bonsai.Core.2.8.5\lib\net462\Bonsai.Core.dll" />
<AssemblyLocation assemblyName="Bonsai.Design" processorArchitecture="MSIL" location="Packages\Bonsai.Design.2.8.5\lib\net462\Bonsai.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Design.Visualizers" processorArchitecture="MSIL" location="Packages\Bonsai.Design.Visualizers.2.8.0\lib\net462\Bonsai.Design.Visualizers.dll" />
<AssemblyLocation assemblyName="Bonsai.Dsp" processorArchitecture="MSIL" location="Packages\Bonsai.Dsp.2.8.1\lib\net462\Bonsai.Dsp.dll" />
<AssemblyLocation assemblyName="Bonsai.Dsp.Design" processorArchitecture="MSIL" location="Packages\Bonsai.Dsp.Design.2.8.0\lib\net462\Bonsai.Dsp.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Editor" processorArchitecture="MSIL" location="Packages\Bonsai.Editor.2.8.5\lib\net472\Bonsai.Editor.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions" processorArchitecture="MSIL" location="Packages\Bonsai.Scripting.Expressions.2.8.0\lib\net462\Bonsai.Scripting.Expressions.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions.Design" processorArchitecture="MSIL" location="Packages\Bonsai.Scripting.Expressions.Design.2.8.0\lib\net462\Bonsai.Scripting.Expressions.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.System" processorArchitecture="MSIL" location="Packages\Bonsai.System.2.8.1\lib\net462\Bonsai.System.dll" />
<AssemblyLocation assemblyName="Bonsai.System.Design" processorArchitecture="MSIL" location="Packages\Bonsai.System.Design.2.8.0\lib\net462\Bonsai.System.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Vision" processorArchitecture="MSIL" location="Packages\Bonsai.Vision.2.8.1\lib\net462\Bonsai.Vision.dll" />
<AssemblyLocation assemblyName="Bonsai.Vision.Design" processorArchitecture="MSIL" location="Packages\Bonsai.Vision.Design.2.8.1\lib\net462\Bonsai.Vision.Design.dll" />
<AssemblyLocation assemblyName="Markdig" processorArchitecture="MSIL" location="Packages\Markdig.0.18.1\lib\net40\Markdig.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Core" processorArchitecture="MSIL" location="Packages\Microsoft.Web.WebView2.1.0.1823.32\lib\net45\Microsoft.Web.WebView2.Core.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.WinForms" processorArchitecture="MSIL" location="Packages\Microsoft.Web.WebView2.1.0.1823.32\lib\net45\Microsoft.Web.WebView2.WinForms.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Wpf" processorArchitecture="MSIL" location="Packages\Microsoft.Web.WebView2.1.0.1823.32\lib\net45\Microsoft.Web.WebView2.Wpf.dll" />
<AssemblyLocation assemblyName="OpenCV.Net" processorArchitecture="MSIL" location="Packages\OpenCV.Net.3.4.2\lib\net462\OpenCV.Net.dll" />
<AssemblyLocation assemblyName="OpenTK" processorArchitecture="MSIL" location="Packages\OpenTK.3.1.0\lib\net20\OpenTK.dll" />
<AssemblyLocation assemblyName="OpenTK.GLControl" processorArchitecture="MSIL" location="Packages\OpenTK.GLControl.3.1.0\lib\net20\OpenTK.GLControl.dll" />
<AssemblyLocation assemblyName="ScintillaNET" processorArchitecture="MSIL" location="Packages\jacobslusser.ScintillaNET.3.6.3\lib\net40\ScintillaNET.dll" />
<AssemblyLocation assemblyName="SVG" processorArchitecture="MSIL" location="Packages\SvgNet.3.3.3\lib\net462\SVG.dll" />
<AssemblyLocation assemblyName="System.Buffers" processorArchitecture="MSIL" location="Packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll" />
<AssemblyLocation assemblyName="System.Linq.Dynamic" processorArchitecture="MSIL" location="Packages\System.Linq.Dynamic.1.0.7\lib\net40\System.Linq.Dynamic.dll" />
<AssemblyLocation assemblyName="System.Memory" processorArchitecture="MSIL" location="Packages\System.Memory.4.5.5\lib\net461\System.Memory.dll" />
<AssemblyLocation assemblyName="System.Numerics.Vectors" processorArchitecture="MSIL" location="Packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll" />
<AssemblyLocation assemblyName="System.Reactive.Core" processorArchitecture="MSIL" location="Packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll" />
<AssemblyLocation assemblyName="System.Reactive.Interfaces" processorArchitecture="MSIL" location="Packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll" />
<AssemblyLocation assemblyName="System.Reactive.Linq" processorArchitecture="MSIL" location="Packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll" />
<AssemblyLocation assemblyName="System.Reactive.PlatformServices" processorArchitecture="MSIL" location="Packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll" />
<AssemblyLocation assemblyName="System.Resources.Extensions" processorArchitecture="MSIL" location="Packages\System.Resources.Extensions.8.0.0\lib\net462\System.Resources.Extensions.dll" />
<AssemblyLocation assemblyName="System.Runtime.CompilerServices.Unsafe" processorArchitecture="MSIL" location="Packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll" />
<AssemblyLocation assemblyName="YamlDotNet" processorArchitecture="MSIL" location="Packages\YamlDotNet.13.1.1\lib\net47\YamlDotNet.dll" />
<AssemblyLocation assemblyName="ZedGraph" processorArchitecture="MSIL" location="Packages\ZedGraph.5.1.7\lib\net35-Client\ZedGraph.dll" />
<AssemblyLocation assemblyName="Bonsai" processorArchitecture="MSIL" location="Packages/Bonsai.2.8.5/lib/net48/Bonsai.exe" />
<AssemblyLocation assemblyName="Bonsai.Core" processorArchitecture="MSIL" location="Packages/Bonsai.Core.2.8.5/lib/net462/Bonsai.Core.dll" />
<AssemblyLocation assemblyName="Bonsai.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Design.2.8.5/lib/net462/Bonsai.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Design.Visualizers" processorArchitecture="MSIL" location="Packages/Bonsai.Design.Visualizers.2.8.0/lib/net462/Bonsai.Design.Visualizers.dll" />
<AssemblyLocation assemblyName="Bonsai.Dsp" processorArchitecture="MSIL" location="Packages/Bonsai.Dsp.2.8.1/lib/net462/Bonsai.Dsp.dll" />
<AssemblyLocation assemblyName="Bonsai.Dsp.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Dsp.Design.2.8.0/lib/net462/Bonsai.Dsp.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Editor" processorArchitecture="MSIL" location="Packages/Bonsai.Editor.2.8.5/lib/net472/Bonsai.Editor.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.2.8.0/lib/net462/Bonsai.Scripting.Expressions.dll" />
<AssemblyLocation assemblyName="Bonsai.Scripting.Expressions.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Scripting.Expressions.Design.2.8.0/lib/net462/Bonsai.Scripting.Expressions.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.System" processorArchitecture="MSIL" location="Packages/Bonsai.System.2.8.1/lib/net462/Bonsai.System.dll" />
<AssemblyLocation assemblyName="Bonsai.System.Design" processorArchitecture="MSIL" location="Packages/Bonsai.System.Design.2.8.0/lib/net462/Bonsai.System.Design.dll" />
<AssemblyLocation assemblyName="Bonsai.Vision" processorArchitecture="MSIL" location="Packages/Bonsai.Vision.2.8.1/lib/net462/Bonsai.Vision.dll" />
<AssemblyLocation assemblyName="Bonsai.Vision.Design" processorArchitecture="MSIL" location="Packages/Bonsai.Vision.Design.2.8.1/lib/net462/Bonsai.Vision.Design.dll" />
<AssemblyLocation assemblyName="Markdig" processorArchitecture="MSIL" location="Packages/Markdig.0.18.1/lib/net40/Markdig.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Core" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Core.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.WinForms" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.WinForms.dll" />
<AssemblyLocation assemblyName="Microsoft.Web.WebView2.Wpf" processorArchitecture="MSIL" location="Packages/Microsoft.Web.WebView2.1.0.1823.32/lib/net45/Microsoft.Web.WebView2.Wpf.dll" />
<AssemblyLocation assemblyName="OpenCV.Net" processorArchitecture="MSIL" location="Packages/OpenCV.Net.3.4.2/lib/net462/OpenCV.Net.dll" />
<AssemblyLocation assemblyName="OpenTK" processorArchitecture="MSIL" location="Packages/OpenTK.3.1.0/lib/net20/OpenTK.dll" />
<AssemblyLocation assemblyName="OpenTK.GLControl" processorArchitecture="MSIL" location="Packages/OpenTK.GLControl.3.1.0/lib/net20/OpenTK.GLControl.dll" />
<AssemblyLocation assemblyName="ScintillaNET" processorArchitecture="MSIL" location="Packages/jacobslusser.ScintillaNET.3.6.3/lib/net40/ScintillaNET.dll" />
<AssemblyLocation assemblyName="SVG" processorArchitecture="MSIL" location="Packages/SvgNet.3.3.3/lib/net462/SVG.dll" />
<AssemblyLocation assemblyName="System.Buffers" processorArchitecture="MSIL" location="Packages/System.Buffers.4.5.1/lib/net461/System.Buffers.dll" />
<AssemblyLocation assemblyName="System.Linq.Dynamic" processorArchitecture="MSIL" location="Packages/System.Linq.Dynamic.1.0.7/lib/net40/System.Linq.Dynamic.dll" />
<AssemblyLocation assemblyName="System.Memory" processorArchitecture="MSIL" location="Packages/System.Memory.4.5.5/lib/net461/System.Memory.dll" />
<AssemblyLocation assemblyName="System.Numerics.Vectors" processorArchitecture="MSIL" location="Packages/System.Numerics.Vectors.4.5.0/lib/net46/System.Numerics.Vectors.dll" />
<AssemblyLocation assemblyName="System.Reactive.Core" processorArchitecture="MSIL" location="Packages/Rx-Core.2.2.5/lib/net45/System.Reactive.Core.dll" />
<AssemblyLocation assemblyName="System.Reactive.Interfaces" processorArchitecture="MSIL" location="Packages/Rx-Interfaces.2.2.5/lib/net45/System.Reactive.Interfaces.dll" />
<AssemblyLocation assemblyName="System.Reactive.Linq" processorArchitecture="MSIL" location="Packages/Rx-Linq.2.2.5/lib/net45/System.Reactive.Linq.dll" />
<AssemblyLocation assemblyName="System.Reactive.PlatformServices" processorArchitecture="MSIL" location="Packages/Rx-PlatformServices.2.2.5/lib/net45/System.Reactive.PlatformServices.dll" />
<AssemblyLocation assemblyName="System.Resources.Extensions" processorArchitecture="MSIL" location="Packages/System.Resources.Extensions.8.0.0/lib/net462/System.Resources.Extensions.dll" />
<AssemblyLocation assemblyName="System.Runtime.CompilerServices.Unsafe" processorArchitecture="MSIL" location="Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/net461/System.Runtime.CompilerServices.Unsafe.dll" />
<AssemblyLocation assemblyName="YamlDotNet" processorArchitecture="MSIL" location="Packages/YamlDotNet.13.1.1/lib/net47/YamlDotNet.dll" />
<AssemblyLocation assemblyName="ZedGraph" processorArchitecture="MSIL" location="Packages/ZedGraph.5.1.7/lib/net35-Client/ZedGraph.dll" />
</AssemblyLocations>
<LibraryFolders>
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-arm64\native" platform="arm64" />
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-arm64\native_uap" platform="arm64" />
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-x64\native" platform="x64" />
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-x64\native_uap" platform="x64" />
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-x86\native" platform="x86" />
<LibraryFolder path="Packages\Microsoft.Web.WebView2.1.0.1823.32\runtimes\win-x86\native_uap" platform="x86" />
<LibraryFolder path="Packages\OpenCV.Net.3.4.2\runtimes\win-x64\native\vc14\bin" platform="x64" />
<LibraryFolder path="Packages\OpenCV.Net.3.4.2\runtimes\win-x86\native\vc14\bin" platform="x86" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-arm64/native" platform="arm64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-arm64/native_uap" platform="arm64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x64/native" platform="x64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x64/native_uap" platform="x64" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native" platform="x86" />
<LibraryFolder path="Packages/Microsoft.Web.WebView2.1.0.1823.32/runtimes/win-x86/native_uap" platform="x86" />
<LibraryFolder path="Packages/OpenCV.Net.3.4.2/runtimes/win-x64/native/vc14/bin" platform="x64" />
<LibraryFolder path="Packages/OpenCV.Net.3.4.2/runtimes/win-x86/native/vc14/bin" platform="x86" />
</LibraryFolders>
</PackageConfiguration>
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.vs/
.vs*/
/artifacts/
.bonsai/Packages/
.bonsai/*.exe
.bonsai/*.exe.settings
.bonsai/*.exe.WebView2/
.bonsai/*.exe.WebView2/
*.user
194 changes: 194 additions & 0 deletions OpenEphys.Onix1.Design/ChannelConfigurationDialog.Designer.cs
1,191 changes: 1,191 additions & 0 deletions OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs

Large diffs are not rendered by default.

1,778 changes: 1,778 additions & 0 deletions OpenEphys.Onix1.Design/ChannelConfigurationDialog.resx

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions OpenEphys.Onix1.Design/ContactTag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Public class used to create tags for contacts in their respective GUIs.
/// </summary>
public class ContactTag
{
const string ContactStringFormat = "Probe_{0}-Contact_{1}";
const string TextStringFormat = "TextProbe_{0}-Contact_{1}";

/// <summary>
/// Gets the probe index of this contact.
/// </summary>
public int ProbeIndex { get; }

/// <summary>
/// Gets the contact index of this contact.
/// </summary>
public int ContactIndex { get; }

/// <summary>
/// Gets the string defining the probe and contact index for this contact.
/// </summary>
public string ContactString => GetContactString(ProbeIndex, ContactIndex);

/// <summary>
/// Gets the string defining the probe and contact index of a text object for this contact.
/// </summary>
public string TextString => GetTextString(ProbeIndex, ContactIndex);

/// <summary>
/// Initializes a new instance of the <see cref="ContactTag"/> class with the given indices.
/// </summary>
/// <param name="probeIndex">Index of the probe for this contact.</param>
/// <param name="contactIndex">Index of the contact for this contact.</param>
public ContactTag(int probeIndex, int contactIndex)
{
ProbeIndex = probeIndex;
ContactIndex = contactIndex;
}

static string GetContactString(int probeNumber, int contactNumber)
{
return string.Format(ContactStringFormat, probeNumber, contactNumber);
}

static string GetTextString(int probeNumber, int contactNumber)
{
return string.Format(TextStringFormat, probeNumber, contactNumber);
}
}

}
147 changes: 147 additions & 0 deletions OpenEphys.Onix1.Design/DesignHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Newtonsoft.Json;

namespace OpenEphys.Onix1.Design
{
static class DesignHelper
{
public static T DeserializeString<T>(string channelLayout)
{
return JsonConvert.DeserializeObject<T>(channelLayout);
}

public static void SerializeObject(object _object, string filepath)
{
var stringJson = JsonConvert.SerializeObject(_object, Formatting.Indented);

File.WriteAllText(filepath, stringJson);
}

public static IEnumerable<Control> GetAllControls(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);

while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}

public static IEnumerable<Control> GetTopLevelControls(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);

if (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
{
yield return child;
}
}
}

/// <summary>
/// Given two forms, take all menu items that are in the "File" MenuItem of the child form, and copy them directly to the
/// "File" MenuItem for the parent form
/// </summary>
/// <param name="thisForm"></param>
/// <param name="childForm"></param>
public static void AddMenuItemsFromDialogToFileOption(this Form thisForm, Form childForm)
{
const string FileString = "File";

if (childForm != null)
{
var childMenuStrip = childForm.GetAllControls()
.OfType<MenuStrip>()
.FirstOrDefault() ?? throw new InvalidOperationException($"There are no menu strips in any child controls of the {childForm.Text} dialog.");

var thisMenuStrip = thisForm.GetTopLevelControls()
.OfType<MenuStrip>()
.FirstOrDefault() ?? throw new InvalidOperationException($"There are no menu strips at the top level of the {thisForm.Text} dialog to pull out.");

ToolStripMenuItem existingMenuItem = null;

foreach (ToolStripMenuItem menuItem in thisMenuStrip.Items)
{
if (menuItem.Text == FileString)
{
existingMenuItem = menuItem;
}
}

foreach (ToolStripMenuItem menuItem in childMenuStrip.Items)
{
if (menuItem.Text == FileString)
{
while (menuItem.DropDownItems.Count > 0)
{
existingMenuItem.DropDownItems.Add(menuItem.DropDownItems[0]);
}
}
}
}
}

/// <summary>
/// Given two forms, take all menu items that are in the "File" MenuItem of the child form, and copy them to the
/// sub-menu name given, nested under the "File" MenuItem for the parent form
/// </summary>
/// <param name="thisForm"></param>
/// <param name="childForm"></param>
/// <param name="subMenuName"></param>
public static void AddMenuItemsFromDialogToFileOption(this Form thisForm, Form childForm, string subMenuName)
{
const string FileString = "File";

if (childForm != null)
{
var childMenuStrip = childForm.GetAllControls()
.OfType<MenuStrip>()
.First() ?? throw new InvalidOperationException($"There are no menu strips in any child controls of the {childForm.Text} dialog.");

var thisMenuStrip = thisForm.GetTopLevelControls()
.OfType<MenuStrip>()
.FirstOrDefault() ?? throw new InvalidOperationException($"There are no menu strips at the top level of the {thisForm.Text} dialog to pull out.");

ToolStripMenuItem thisFileMenuItem = null;

foreach (ToolStripMenuItem menuItem in thisMenuStrip.Items)
{
if (menuItem.Text == FileString)
{
thisFileMenuItem = menuItem;
}
}

ToolStripMenuItem newChildMenuItems = new()
{
Text = subMenuName
};

foreach (ToolStripMenuItem childItem in childMenuStrip.Items)
{
if (childItem.Text == FileString)
{
while (childItem.DropDownItems.Count > 0)
{
newChildMenuItems.DropDownItems.Add(childItem.DropDownItems[0]);
}
}
}

thisFileMenuItem.DropDownItems.Add(newChildMenuItems);
}
}
}
}
126 changes: 126 additions & 0 deletions OpenEphys.Onix1.Design/GenericDeviceDialog.Designer.cs
34 changes: 34 additions & 0 deletions OpenEphys.Onix1.Design/GenericDeviceDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Windows.Forms;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Abstract form that implements a very basic GUI consisting of a single property grid and
/// two buttons (OK / Cancel).
/// </summary>
public abstract partial class GenericDeviceDialog : Form
{
/// <summary>
/// Initializes a new instance of <see cref="GenericDeviceDialog"/>.
/// </summary>
public GenericDeviceDialog()
{
InitializeComponent();
}

private void ButtonClick(object sender, System.EventArgs e)
{
if (sender is Button button)
{
if (button.Name == nameof(buttonOK))
{
DialogResult = DialogResult.OK;
}
else if (button.Name == nameof(buttonCancel))
{
DialogResult = DialogResult.Cancel;
}
}
}
}
}
1,775 changes: 1,775 additions & 0 deletions OpenEphys.Onix1.Design/GenericDeviceDialog.resx

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eBno055Dialog.Designer.cs
42 changes: 42 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eBno055Dialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Partial class to create a GUI for <see cref="ConfigureNeuropixelsV2eBno055"/>.
/// </summary>
public partial class NeuropixelsV2eBno055Dialog : GenericDeviceDialog
{
/// <summary>
/// Gets or sets the <see cref="ConfigureNeuropixelsV2eBno055"/> object attached to
/// the property grid.
/// </summary>
public ConfigureNeuropixelsV2eBno055 ConfigureNode
{
get => (ConfigureNeuropixelsV2eBno055)propertyGrid.SelectedObject;
set => propertyGrid.SelectedObject = value;
}

/// <summary>
/// Initializes a new instance of <see cref="NeuropixelsV2eBno055Dialog"/> with the given
/// <see cref="ConfigureNeuropixelsV2eBno055"/> object.
/// </summary>
/// <param name="configureNode">A <see cref="ConfigureNeuropixelsV2eBno055"/> object that contains configuration settings.</param>
public NeuropixelsV2eBno055Dialog(ConfigureNeuropixelsV2eBno055 configureNode)
{
InitializeComponent();
Shown += FormShown;

ConfigureNode = new(configureNode);
}

private void FormShown(object sender, EventArgs e)
{
if (!TopLevel)
{
splitContainer1.Panel2Collapsed = true;
splitContainer1.Panel2.Hide();
}
}
}
}
35 changes: 35 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eBno055Editor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.ComponentModel;
using Bonsai.Design;
using System.Windows.Forms;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Class that opens a new dialog for a <see cref="ConfigureNeuropixelsV2eBno055"/>.
/// </summary>
public class NeuropixelsV2eBno055Editor : WorkflowComponentEditor
{
/// <inheritdoc/>
public override bool EditComponent(ITypeDescriptorContext context, object component, IServiceProvider provider, IWin32Window owner)
{
if (provider != null)
{
var editorState = (IWorkflowEditorState)provider.GetService(typeof(IWorkflowEditorState));
if (editorState != null && !editorState.WorkflowRunning && component is ConfigureNeuropixelsV2eBno055 configureBno055)
{
using var editorDialog = new NeuropixelsV2eBno055Dialog(configureBno055);

if (editorDialog.ShowDialog() == DialogResult.OK)
{
configureBno055.Enable = editorDialog.ConfigureNode.Enable;

return true;
}
}
}

return false;
}
}
}
262 changes: 262 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eChannelConfigurationDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using OpenEphys.ProbeInterface.NET;
using ZedGraph;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Partial class to create a channel configuration GUI for <see cref="ConfigureNeuropixelsV2e"/>.
/// </summary>
public partial class NeuropixelsV2eChannelConfigurationDialog : ChannelConfigurationDialog
{
internal event EventHandler OnZoom;
internal event EventHandler OnFileLoad;

/// <summary>
/// Public <see cref="NeuropixelsV2QuadShankProbeConfiguration"/> object that is manipulated by
/// <see cref="NeuropixelsV2eChannelConfigurationDialog"/>.
/// </summary>
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfiguration;

/// <summary>
/// Initializes a new instance of <see cref="NeuropixelsV2eChannelConfigurationDialog"/>.
/// </summary>
/// <param name="probeConfiguration">A <see cref="NeuropixelsV2QuadShankProbeConfiguration"/> object holding the current configuration settings.</param>
public NeuropixelsV2eChannelConfigurationDialog(NeuropixelsV2QuadShankProbeConfiguration probeConfiguration)
: base(probeConfiguration.ChannelConfiguration)
{
zedGraphChannels.ZoomButtons = MouseButtons.None;
zedGraphChannels.ZoomButtons2 = MouseButtons.None;

zedGraphChannels.ZoomStepFraction = 0.5;

ProbeConfiguration = probeConfiguration;

ZoomInBoundaryX = 600;
ZoomInBoundaryY = 600;

HighlightEnabledContacts();
UpdateContactLabels();
RefreshZedGraph();
}

internal override ProbeGroup DefaultChannelLayout()
{
return new NeuropixelsV2eProbeGroup();
}

internal override void LoadDefaultChannelLayout()
{
ProbeConfiguration = new(ProbeConfiguration.Probe, ProbeConfiguration.Reference);
ChannelConfiguration = ProbeConfiguration.ChannelConfiguration;

DrawProbeGroup();
RefreshZedGraph();

OnFileOpenHandler();
}

internal override void OpenFile<T>()
{
var newConfiguration = OpenAndParseConfigurationFile<NeuropixelsV2eProbeGroup>();

if (newConfiguration == null)
{
return;
}

if (ProbeConfiguration.ChannelConfiguration.NumberOfContacts == newConfiguration.NumberOfContacts)
{
newConfiguration.Validate();

ProbeConfiguration = new(newConfiguration, ProbeConfiguration.Reference, ProbeConfiguration.Probe);
ChannelConfiguration = ProbeConfiguration.ChannelConfiguration;

DrawProbeGroup();
RefreshZedGraph();
}
else
{
throw new InvalidOperationException($"Number of contacts does not match; expected {ProbeConfiguration.ChannelConfiguration.NumberOfContacts} contacts" +
$", but found {newConfiguration.NumberOfContacts} contacts");
}

OnFileOpenHandler();
}

private void OnFileOpenHandler()
{
OnFileLoad?.Invoke(this, EventArgs.Empty);
}

internal override void ZoomEvent(ZedGraphControl sender, ZoomState oldState, ZoomState newState)
{
base.ZoomEvent(sender, oldState, newState);

UpdateFontSize();
DrawScale();
RefreshZedGraph();

OnZoomHandler();
}

private void OnZoomHandler()
{
OnZoom?.Invoke(this, EventArgs.Empty);
}

internal override void DrawScale()
{
if (ProbeConfiguration == null)
return;

const string ScalePointsTag = "scale_points";
const string ScaleTextTag = "scale_text";

zedGraphChannels.GraphPane.GraphObjList.RemoveAll(obj => obj is TextObj && obj.Tag is string tag && tag == ScaleTextTag);
zedGraphChannels.GraphPane.CurveList.RemoveAll(curve => curve.Tag is string tag && tag == ScalePointsTag);

const int MajorTickIncrement = 100;
const int MajorTickLength = 10;
const int MinorTickIncrement = 10;
const int MinorTickLength = 5;

if (ProbeConfiguration.ChannelConfiguration.Probes.ElementAt(0).SiUnits != ProbeSiUnits.um)
{
MessageBox.Show("Warning: Expected ProbeGroup units to be in microns, but it is in millimeters. Scale might not be accurate.");
}

var fontSize = CalculateFontSize();

var zoomedOut = fontSize <= 2;

fontSize = zoomedOut ? 8 : fontSize * 4;
var majorTickOffset = MajorTickLength + CalculateScaleRange(zedGraphChannels.GraphPane.XAxis.Scale) * 0.015;
majorTickOffset = majorTickOffset > 50 ? 50 : majorTickOffset;

var x = GetProbeContourMaxX(zedGraphChannels.GraphPane.GraphObjList) + 50;
var minY = GetProbeContourMinY(zedGraphChannels.GraphPane.GraphObjList);
var maxY = GetProbeContourMaxY(zedGraphChannels.GraphPane.GraphObjList);

PointPairList pointList = new();

var countMajorTicks = 0;

for (int i = (int)minY; i < maxY; i += MajorTickIncrement)
{
PointPair majorTickLocation = new(x + majorTickOffset, minY + MajorTickIncrement * countMajorTicks);

pointList.Add(new PointPair(x, minY + MajorTickIncrement * countMajorTicks));
pointList.Add(majorTickLocation);
pointList.Add(new PointPair(x, minY + MajorTickIncrement * countMajorTicks));

if (!zoomedOut || i % (5 * MajorTickIncrement) == 0)
{
TextObj textObj = new($"{i} µm\n", majorTickLocation.X + 5, majorTickLocation.Y, CoordType.AxisXYScale, AlignH.Left, AlignV.Center)
{
Tag = ScaleTextTag,
};
textObj.FontSpec.Border.IsVisible = false;
textObj.FontSpec.Size = fontSize;
zedGraphChannels.GraphPane.GraphObjList.Add(textObj);
}

if (!zoomedOut)
{
var countMinorTicks = 1;

for (int j = i + MinorTickIncrement; j < i + MajorTickIncrement && i + MinorTickIncrement * countMinorTicks < maxY; j += MinorTickIncrement)
{
pointList.Add(new PointPair(x, minY + MajorTickIncrement * countMajorTicks + MinorTickIncrement * countMinorTicks));
pointList.Add(new PointPair(x + MinorTickLength, minY + MajorTickIncrement * countMajorTicks + MinorTickIncrement * countMinorTicks));
pointList.Add(new PointPair(x, minY + MajorTickIncrement * countMajorTicks + MinorTickIncrement * countMinorTicks));

countMinorTicks++;
}
}

countMajorTicks++;
}

var curve = zedGraphChannels.GraphPane.AddCurve(ScalePointsTag, pointList, Color.Black, SymbolType.None);

const float scaleBarWidth = 1;

curve.Line.Width = scaleBarWidth;
curve.Label.IsVisible = false;
curve.Symbol.IsVisible = false;
}

internal override void HighlightEnabledContacts()
{
if (ProbeConfiguration == null || ProbeConfiguration.ChannelMap == null)
return;

var contactObjects = zedGraphChannels.GraphPane.GraphObjList.OfType<BoxObj>()
.Where(c => c is not PolyObj);

var enabledContacts = contactObjects.Where(c => c.Fill.Color == EnabledContactFill);

foreach (var contact in enabledContacts)
{
contact.Fill.Color = DisabledContactFill;
}

var contactsToEnable = contactObjects.Where(c =>
{
var tag = c.Tag as ContactTag;
var channel = NeuropixelsV2QuadShankElectrode.GetChannelNumber(tag.ContactIndex);
return ProbeConfiguration.ChannelMap[channel].Index == tag.ContactIndex;
});

foreach (var contact in contactsToEnable)
{
var tag = (ContactTag)contact.Tag;

contact.Fill.Color = ReferenceContacts.Any(x => x == tag.ContactIndex) ? ReferenceContactFill : EnabledContactFill;
}
}

internal override void UpdateContactLabels()
{
if (ProbeConfiguration.ChannelConfiguration == null)
return;

var textObjs = zedGraphChannels.GraphPane.GraphObjList.OfType<TextObj>()
.Where(t => t.Tag is ContactTag);

var textObjsToUpdate = textObjs.Where(t => t.FontSpec.FontColor != DisabledContactTextColor);

foreach (var textObj in textObjsToUpdate)
{
textObj.FontSpec.FontColor = DisabledContactTextColor;
}

textObjsToUpdate = textObjs.Where(c =>
{
var tag = c.Tag as ContactTag;
var channel = NeuropixelsV2QuadShankElectrode.GetChannelNumber(tag.ContactIndex);
return ProbeConfiguration.ChannelMap[channel].Index == tag.ContactIndex;
});

foreach (var textObj in textObjsToUpdate)
{
textObj.FontSpec.FontColor = EnabledContactTextColor;
}
}

internal override string ContactString(int deviceChannelIndex, int index)
{
return index.ToString();
}

internal void EnableElectrodes(List<NeuropixelsV2QuadShankElectrode> electrodes)
{
ProbeConfiguration.SelectElectrodes(electrodes);
}
}
}
167 changes: 167 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eDialog.Designer.cs
120 changes: 120 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Partial class to create a GUI for <see cref="ConfigureNeuropixelsV2e"/>.
/// </summary>
public partial class NeuropixelsV2eDialog : Form
{
readonly IReadOnlyList<NeuropixelsV2eProbeConfigurationDialog> ProbeConfigurations;

/// <summary>
/// Public <see cref="ConfigureNeuropixelsV2e"/> object that is manipulated by
/// <see cref="NeuropixelsV2eDialog"/>.
/// </summary>
public ConfigureNeuropixelsV2e ConfigureNode { get; set; }

/// <summary>
/// Initializes a new instance of <see cref="NeuropixelsV2eDialog"/>.
/// </summary>
/// <param name="configureNode">A <see cref="ConfigureNeuropixelsV2e"/> object holding the current configuration settings.</param>
public NeuropixelsV2eDialog(ConfigureNeuropixelsV2e configureNode)
{
InitializeComponent();
Shown += FormShown;

ConfigureNode = new(configureNode);

ProbeConfigurations = new List<NeuropixelsV2eProbeConfigurationDialog>
{
new(ConfigureNode.ProbeConfigurationA, ConfigureNode.GainCalibrationFileA)
{
TopLevel = false,
FormBorderStyle = FormBorderStyle.None,
Dock = DockStyle.Fill,
Parent = this,
Tag = NeuropixelsV2Probe.ProbeA
},
new(ConfigureNode.ProbeConfigurationB, ConfigureNode.GainCalibrationFileB)
{
TopLevel = false,
FormBorderStyle = FormBorderStyle.None,
Dock = DockStyle.Fill,
Parent = this,
Tag = NeuropixelsV2Probe.ProbeB
}
};

foreach (var channelConfiguration in ProbeConfigurations)
{
string probeName = GetProbeName((NeuropixelsV2Probe)channelConfiguration.Tag);

tabControlProbe.TabPages.Add(probeName, probeName);
tabControlProbe.TabPages[probeName].Controls.Add(channelConfiguration);
this.AddMenuItemsFromDialogToFileOption(channelConfiguration, probeName);
}
}

private string GetProbeName(NeuropixelsV2Probe probe)
{
return probe switch
{
NeuropixelsV2Probe.ProbeA => "Probe A",
NeuropixelsV2Probe.ProbeB => "Probe B",
_ => throw new ArgumentException("Invalid probe was specified.")
};
}

private int GetProbeIndex(NeuropixelsV2Probe probe)
{
return probe == NeuropixelsV2Probe.ProbeA ? 0 : 1;
}

private void FormShown(object sender, EventArgs e)
{
if (!TopLevel)
{
splitContainer1.Panel2Collapsed = true;
splitContainer1.Panel2.Hide();

menuStrip.Visible = false;
}

foreach (var channelConfiguration in ProbeConfigurations)
{
channelConfiguration.Show();
}
}

internal void ButtonClick(object sender, EventArgs e)
{
if (sender is Button button && button != null)
{
if (button.Name == nameof(buttonOkay))
{
SaveVariables();

DialogResult = DialogResult.OK;
}
else if (button.Name == nameof(buttonCancel))
{
DialogResult = DialogResult.Cancel;
}
}
}

internal void SaveVariables()
{
ConfigureNode.ProbeConfigurationA = ProbeConfigurations[GetProbeIndex(NeuropixelsV2Probe.ProbeA)].ProbeConfiguration;
ConfigureNode.ProbeConfigurationB = ProbeConfigurations[GetProbeIndex(NeuropixelsV2Probe.ProbeB)].ProbeConfiguration;

ConfigureNode.GainCalibrationFileA = ProbeConfigurations[GetProbeIndex(NeuropixelsV2Probe.ProbeA)].textBoxProbeCalibrationFile.Text;
ConfigureNode.GainCalibrationFileB = ProbeConfigurations[GetProbeIndex(NeuropixelsV2Probe.ProbeB)].textBoxProbeCalibrationFile.Text;
}
}
}
1,778 changes: 1,778 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eDialog.resx

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using Bonsai.Design;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Class that opens a new dialog for a <see cref="ConfigureNeuropixelsV2e"/>.
/// </summary>
public class NeuropixelsV2eEditor : WorkflowComponentEditor
{
/// <inheritdoc/>
public override bool EditComponent(ITypeDescriptorContext context, object component, IServiceProvider provider, IWin32Window owner)
{
if (provider != null)
{
var editorState = (IWorkflowEditorState)provider.GetService(typeof(IWorkflowEditorState));
if (editorState != null && !editorState.WorkflowRunning && component is ConfigureNeuropixelsV2e configureNeuropixelsV2e)
{
using var editorDialog = new NeuropixelsV2eDialog(configureNeuropixelsV2e);

if (editorDialog.ShowDialog() == DialogResult.OK)
{
configureNeuropixelsV2e.Enable = editorDialog.ConfigureNode.Enable;
configureNeuropixelsV2e.GainCalibrationFileA = editorDialog.ConfigureNode.GainCalibrationFileA;
configureNeuropixelsV2e.GainCalibrationFileB = editorDialog.ConfigureNode.GainCalibrationFileB;
configureNeuropixelsV2e.ProbeConfigurationA = editorDialog.ConfigureNode.ProbeConfigurationA;
configureNeuropixelsV2e.ProbeConfigurationB = editorDialog.ConfigureNode.ProbeConfigurationB;

return true;
}
}
}

return false;
}
}
}
211 changes: 211 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eHeadstageDialog.Designer.cs
67 changes: 67 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eHeadstageDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Windows.Forms;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Partial class to create a GUI for <see cref="ConfigureNeuropixelsV2eHeadstage"/>.
/// </summary>
public partial class NeuropixelsV2eHeadstageDialog : Form
{
/// <summary>
/// A <see cref="NeuropixelsV2eDialog"/> that configures a <see cref="ConfigureNeuropixelsV2e"/>.
/// </summary>
public readonly NeuropixelsV2eDialog DialogNeuropixelsV2e;

/// <summary>
/// A <see cref="NeuropixelsV2eBno055Dialog"/> that configures a <see cref="ConfigureNeuropixelsV2eBno055"/>.
/// </summary>
public readonly NeuropixelsV2eBno055Dialog DialogBno055;

/// <summary>
/// Initializes a new instance of a <see cref="NeuropixelsV2eHeadstageDialog"/>.
/// </summary>
/// <param name="configureNeuropixelsV2e">Configuration settings for a <see cref="ConfigureNeuropixelsV2e"/>.</param>
/// <param name="configureBno055">Configuration settings for a <see cref="ConfigureNeuropixelsV2eBno055"/>.</param>
public NeuropixelsV2eHeadstageDialog(ConfigureNeuropixelsV2e configureNeuropixelsV2e, ConfigureNeuropixelsV2eBno055 configureBno055)
{
InitializeComponent();

DialogNeuropixelsV2e = new(configureNeuropixelsV2e)
{
TopLevel = false,
FormBorderStyle = FormBorderStyle.None,
Dock = DockStyle.Fill,
Parent = this
};

panelNeuropixelsV2e.Controls.Add(DialogNeuropixelsV2e);
this.AddMenuItemsFromDialogToFileOption(DialogNeuropixelsV2e, "NeuropixelsV2e");
DialogNeuropixelsV2e.Show();

DialogBno055 = new(configureBno055)
{
TopLevel = false,
FormBorderStyle = FormBorderStyle.None,
Dock = DockStyle.Fill,
Parent = this
};

panelBno055.Controls.Add(DialogBno055);
DialogBno055.Show();
DialogBno055.Invalidate();
}

private void ButtonClick(object sender, System.EventArgs e)
{
if (sender is Button button && button != null)
{
if (button.Name == nameof(buttonOkay))
{
DialogNeuropixelsV2e.SaveVariables();

DialogResult = DialogResult.OK;
}
}
}
}
}
1,778 changes: 1,778 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eHeadstageDialog.resx

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eHeadstageEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Bonsai.Design;
using System.ComponentModel;
using System.Windows.Forms;
using System;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Class that opens a new dialog for a <see cref="ConfigureNeuropixelsV2eHeadstage"/>.
/// </summary>
public class NeuropixelsV2eHeadstageEditor : WorkflowComponentEditor
{
/// <inheritdoc/>
public override bool EditComponent(ITypeDescriptorContext context, object component, IServiceProvider provider, IWin32Window owner)
{
if (provider != null)
{
var editorState = (IWorkflowEditorState)provider.GetService(typeof(IWorkflowEditorState));
if (editorState != null && !editorState.WorkflowRunning && component is ConfigureNeuropixelsV2eHeadstage configureHeadstage)
{
using var editorDialog = new NeuropixelsV2eHeadstageDialog(configureHeadstage.NeuropixelsV2e, configureHeadstage.Bno055);

if (editorDialog.ShowDialog() == DialogResult.OK)
{
configureHeadstage.Bno055.Enable = editorDialog.DialogBno055.ConfigureNode.Enable;

configureHeadstage.NeuropixelsV2e.Enable = editorDialog.DialogNeuropixelsV2e.ConfigureNode.Enable;
configureHeadstage.NeuropixelsV2e.ProbeConfigurationA = editorDialog.DialogNeuropixelsV2e.ConfigureNode.ProbeConfigurationA;
configureHeadstage.NeuropixelsV2e.ProbeConfigurationB = editorDialog.DialogNeuropixelsV2e.ConfigureNode.ProbeConfigurationB;
configureHeadstage.NeuropixelsV2e.GainCalibrationFileA = editorDialog.DialogNeuropixelsV2e.ConfigureNode.GainCalibrationFileA;
configureHeadstage.NeuropixelsV2e.GainCalibrationFileB = editorDialog.DialogNeuropixelsV2e.ConfigureNode.GainCalibrationFileB;

return true;
}
}
}

return false;
}
}
}

Large diffs are not rendered by default.

619 changes: 619 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.cs

Large diffs are not rendered by default.

1,796 changes: 1,796 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationDialog.resx

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions OpenEphys.Onix1.Design/NeuropixelsV2eProbeConfigurationEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using Bonsai.Design;

namespace OpenEphys.Onix1.Design
{
/// <summary>
/// Class that opens a new dialog for a <see cref="NeuropixelsV2QuadShankProbeConfiguration"/>.
/// </summary>
public class NeuropixelsV2eProbeConfigurationEditor : UITypeEditor
{
/// <inheritdoc/>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}

/// <inheritdoc/>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
var editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
var editorState = (IWorkflowEditorState)provider.GetService(typeof(IWorkflowEditorState));

if (editorService != null && editorState != null && !editorState.WorkflowRunning &&
value is NeuropixelsV2QuadShankProbeConfiguration configuration)
{
var instance = (ConfigureNeuropixelsV2e)context.Instance;

var calibrationFile = configuration.Probe == NeuropixelsV2Probe.ProbeA ? instance.GainCalibrationFileA : instance.GainCalibrationFileB;

using var editorDialog = new NeuropixelsV2eProbeConfigurationDialog(configuration, calibrationFile);

if (editorDialog.ShowDialog() == DialogResult.OK)
{
if (configuration.Probe == NeuropixelsV2Probe.ProbeA)
{
instance.GainCalibrationFileA = editorDialog.textBoxProbeCalibrationFile.Text;
}
else
{
instance.GainCalibrationFileB = editorDialog.textBoxProbeCalibrationFile.Text;
}

return editorDialog.ProbeConfiguration;
}
}
}

return base.EditValue(context, provider, value);
}
}
}
24 changes: 23 additions & 1 deletion OpenEphys.Onix1.Design/OpenEphys.Onix1.Design.csproj
Original file line number Diff line number Diff line change
@@ -6,12 +6,34 @@
<PackageTags>Bonsai Rx Open Ephys Onix Design</PackageTags>
<TargetFramework>net472</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<IsPackable>false</IsPackable>
<Platforms>x64</Platforms>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Bonsai.Design" Version="2.8.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="OpenEphys.ProbeInterface.NET" Version="0.1.0" />
<PackageReference Include="ZedGraph" Version="5.1.7" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OpenEphys.Onix1\OpenEphys.Onix1.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

</Project>
63 changes: 63 additions & 0 deletions OpenEphys.Onix1.Design/Properties/Resources.Designer.cs
120 changes: 120 additions & 0 deletions OpenEphys.Onix1.Design/Properties/Resources.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
Binary file added OpenEphys.Onix1.Design/icon.ico
Binary file not shown.
38 changes: 36 additions & 2 deletions OpenEphys.Onix1/ConfigureNeuropixelsV2e.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Reactive.Disposables;
using Bonsai;

@@ -8,6 +9,7 @@ namespace OpenEphys.Onix1
/// <summary>
/// A class that configures a NeuropixelsV2e device.
/// </summary>
[Editor("OpenEphys.Onix1.Design.NeuropixelsV2eEditor, OpenEphys.Onix1.Design", typeof(ComponentEditor))]
[Description("Configures a NeuropixelsV2e device.")]
public class ConfigureNeuropixelsV2e : SingleDeviceFactory
{
@@ -19,6 +21,22 @@ public ConfigureNeuropixelsV2e()
{
}

/// <summary>
/// Copy constructor for the <see cref="ConfigureNeuropixelsV2e"/> class.
/// </summary>
/// <param name="configureNode">A pre-existing <see cref="ConfigureNeuropixelsV2e"/> object.</param>
public ConfigureNeuropixelsV2e(ConfigureNeuropixelsV2e configureNode)
: base(typeof(NeuropixelsV2e))
{
Enable = configureNode.Enable;
ProbeConfigurationA = configureNode.ProbeConfigurationA;
ProbeConfigurationB = configureNode.ProbeConfigurationB;
GainCalibrationFileA = configureNode.GainCalibrationFileA;
GainCalibrationFileB = configureNode.GainCalibrationFileB;
DeviceName = configureNode.DeviceName;
DeviceAddress = configureNode.DeviceAddress;
}

/// <summary>
/// Gets or sets the device enable state.
/// </summary>
@@ -33,9 +51,16 @@ public ConfigureNeuropixelsV2e()
/// <summary>
/// Gets or sets the electrode configuration for Probe A.
/// </summary>
/// <remarks>
/// Configuration is accomplished using a GUI to aid in channel selection and relevant configuration properties.
/// To open a probe configuration GUI, select the ellipses next the <see cref="ProbeConfigurationA"/> variable
/// in the property pane, or double-click <see cref="ConfigureNeuropixelsV2eHeadstage"/> to configure both
/// probes and the <see cref="ConfigureNeuropixelsV2eBno055"/> simultaneously.
/// </remarks>
[Category(ConfigurationCategory)]
[Description("Probe A electrode configuration.")]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new();
[Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA);

/// <summary>
/// Gets or sets the path to the gain calibration file for Probe A.
@@ -52,6 +77,7 @@ public ConfigureNeuropixelsV2e()
/// file for your probe, email IMEC at neuropixels.info@imec.be with the probe serial number to retrieve a new copy.
/// </para>
/// </remarks>
[Category(ConfigurationCategory)]
[FileNameFilter("Gain calibration files (*_gainCalValues.csv)|*_gainCalValues.csv")]
[Description("Path to the gain calibration file for probe A.")]
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
@@ -60,9 +86,16 @@ public ConfigureNeuropixelsV2e()
/// <summary>
/// Gets or sets the electrode configuration for Probe B.
/// </summary>
/// <remarks>
/// Configuration is accomplished using a GUI to aid in channel selection and relevant configuration properties.
/// To open a probe configuration GUI, select the ellipses next the <see cref="ProbeConfigurationB"/> variable
/// in the property pane, or double-click <see cref="ConfigureNeuropixelsV2eHeadstage"/> to configure both
/// probes and the <see cref="ConfigureNeuropixelsV2eBno055"/> simultaneously.
/// </remarks>
[Category(ConfigurationCategory)]
[Description("Probe B electrode configuration.")]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new();
[Editor("OpenEphys.Onix1.Design.NeuropixelsV2eProbeConfigurationEditor, OpenEphys.Onix1.Design", typeof(UITypeEditor))]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB);

/// <summary>
/// Gets or sets the path to the gain calibration file for Probe B.
@@ -79,6 +112,7 @@ public ConfigureNeuropixelsV2e()
/// file for your probe, email IMEC at neuropixels.info@imec.be with the probe serial number to retrieve a new copy.
/// </para>
/// </remarks>
[Category(ConfigurationCategory)]
[FileNameFilter("Gain calibration files (*_gainCalValues.csv)|*_gainCalValues.csv")]
[Description("Path to the gain calibration file for probe B.")]
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
6 changes: 4 additions & 2 deletions OpenEphys.Onix1/ConfigureNeuropixelsV2eBeta.cs
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ public ConfigureNeuropixelsV2eBeta()
/// </summary>
[Category(ConfigurationCategory)]
[Description("Probe A electrode configuration.")]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new();
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationA { get; set; } = new(NeuropixelsV2Probe.ProbeA);

/// <summary>
/// Gets or sets the path to the gain calibration file for Probe A.
@@ -62,6 +62,7 @@ public ConfigureNeuropixelsV2eBeta()
/// file for your probe, email IMEC at neuropixels.info@imec.be with the probe serial number to retrieve a new copy.
/// </para>
/// </remarks>
[Category(ConfigurationCategory)]
[FileNameFilter("Gain calibration files (*_gainCalValues.csv)|*_gainCalValues.csv")]
[Description("Path to the gain calibration file for probe A.")]
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
@@ -72,7 +73,7 @@ public ConfigureNeuropixelsV2eBeta()
/// </summary>
[Category(ConfigurationCategory)]
[Description("Probe B electrode configuration.")]
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new();
public NeuropixelsV2QuadShankProbeConfiguration ProbeConfigurationB { get; set; } = new(NeuropixelsV2Probe.ProbeB);

/// <summary>
/// Gets or sets the path to the gain calibration file for Probe B.
@@ -89,6 +90,7 @@ public ConfigureNeuropixelsV2eBeta()
/// file for your probe, email IMEC at neuropixels.info@imec.be with the probe serial number to retrieve a new copy.
/// </para>
/// </remarks>
[Category(ConfigurationCategory)]
[FileNameFilter("Gain calibration files (*_gainCalValues.csv)|*_gainCalValues.csv")]
[Description("Path to the gain calibration file for probe B.")]
[Editor("Bonsai.Design.OpenFileNameEditor, Bonsai.Design", DesignTypes.UITypeEditor)]
13 changes: 13 additions & 0 deletions OpenEphys.Onix1/ConfigureNeuropixelsV2eBno055.cs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ namespace OpenEphys.Onix1
/// <summary>
/// A class that configures a NeuropixelsV2eBno055 device.
/// </summary>
[Editor("OpenEphys.Onix1.Design.NeuropixelsV2eBno055Editor, OpenEphys.Onix1.Design", typeof(ComponentEditor))]
[Description("Configures a NeuropixelsV2eBno055 device.")]
public class ConfigureNeuropixelsV2eBno055 : SingleDeviceFactory
{
@@ -17,6 +18,18 @@ public ConfigureNeuropixelsV2eBno055()
{
}

/// <summary>
/// Copy constructor for the <see cref="ConfigureNeuropixelsV2eBno055"/> class.
/// </summary>
/// <param name="configureNeuropixelsV2eBno055">A pre-existing <see cref="ConfigureNeuropixelsV2eBno055"/> object.</param>
public ConfigureNeuropixelsV2eBno055(ConfigureNeuropixelsV2eBno055 configureNeuropixelsV2eBno055)
: base(typeof(NeuropixelsV2eBno055))
{
Enable = configureNeuropixelsV2eBno055.Enable;
DeviceName = configureNeuropixelsV2eBno055.DeviceName;
DeviceAddress = configureNeuropixelsV2eBno055.DeviceAddress;
}

/// <summary>
/// Gets or sets the device enable state.
/// </summary>
1 change: 1 addition & 0 deletions OpenEphys.Onix1/ConfigureNeuropixelsV2eHeadstage.cs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ namespace OpenEphys.Onix1
/// <summary>
/// A class that configures a NeuropixelsV2e headstage.
/// </summary>
[Editor("OpenEphys.Onix1.Design.NeuropixelsV2eHeadstageEditor, OpenEphys.Onix1.Design", typeof(ComponentEditor))]
[Description("configures a NeuropixelsV2e headstage.")]
public class ConfigureNeuropixelsV2eHeadstage : MultiDeviceFactory
{
42 changes: 42 additions & 0 deletions OpenEphys.Onix1/Electrode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Drawing;
using System.Xml.Serialization;

namespace OpenEphys.Onix1
{
/// <summary>
/// Abstract base class for describing a single electrode.
/// </summary>
public abstract class Electrode
{
/// <summary>
/// Gets the index of the electrode (the electrode "number") within
/// the context of the entire probe.
/// </summary>
[XmlIgnore]
public int Index { get; internal set; }

/// <summary>
/// Gets the shank this electrode belongs to.
/// </summary>
[XmlIgnore]
public int Shank { get; internal set; }

/// <summary>
/// Gets the index of the electrode within the context of <see cref="Shank"/>.
/// </summary>
[XmlIgnore]
public int IntraShankElectrodeIndex { get; internal set; }

/// <summary>
/// Gets the electrical channel that this electrode is mapped to.
/// </summary>
[XmlIgnore]
public int Channel { get; internal set; }

/// <summary>
/// Gets the location of the electrode in two-dimensional space in arbitrary units.
/// </summary>
[XmlIgnore]
public PointF Position { get; internal set; }
}
}
19 changes: 10 additions & 9 deletions OpenEphys.Onix1/NeuropixelsV2.cs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ static class NeuropixelsV2
public const int ChannelCount = 384;
public const int BaseBitsPerChannel = 4;
public const int ElectrodePerShank = 1280;
public const int ElectrodePerBlock = 48;
public const int ReferencePixelCount = 4;
public const int DummyRegisterCount = 4;
public const int RegistersPerShank = ElectrodePerShank + ReferencePixelCount + DummyRegisterCount;
@@ -38,10 +39,10 @@ internal static BitArray[] GenerateShankBits(NeuropixelsV2QuadShankProbeConfigur
{
BitArray[] shankBits =
{
new(NeuropixelsV2.RegistersPerShank, false),
new(NeuropixelsV2.RegistersPerShank, false),
new(NeuropixelsV2.RegistersPerShank, false),
new(NeuropixelsV2.RegistersPerShank, false)
new(RegistersPerShank, false),
new(RegistersPerShank, false),
new(RegistersPerShank, false),
new(RegistersPerShank, false)
};


@@ -68,7 +69,7 @@ internal static BitArray[] GenerateShankBits(NeuropixelsV2QuadShankProbeConfigur
shankBits[3][1285] = true;
}

const int PixelOffset = (NeuropixelsV2.ElectrodePerShank - 1) / 2;
const int PixelOffset = (ElectrodePerShank - 1) / 2;
const int ReferencePixelOffset = 3;
foreach (var c in probe.ChannelMap)
{
@@ -88,8 +89,8 @@ internal static BitArray[] GenerateBaseBits(NeuropixelsV2QuadShankProbeConfigura
{
BitArray[] baseBits =
{
new(NeuropixelsV2.ChannelCount * NeuropixelsV2.BaseBitsPerChannel / 2, false),
new(NeuropixelsV2.ChannelCount * NeuropixelsV2.BaseBitsPerChannel / 2, false)
new(ChannelCount * BaseBitsPerChannel / 2, false),
new(ChannelCount * BaseBitsPerChannel / 2, false)
};

var referenceBit = probe.Reference switch
@@ -102,10 +103,10 @@ internal static BitArray[] GenerateBaseBits(NeuropixelsV2QuadShankProbeConfigura
_ => throw new InvalidOperationException("Invalid reference selection."),
};

for (int i = 0; i < NeuropixelsV2.ChannelCount; i++)
for (int i = 0; i < ChannelCount; i++)
{
var configIndex = i % 2;
var bitOffset = (382 - i + configIndex) / 2 * NeuropixelsV2.BaseBitsPerChannel;
var bitOffset = (382 - i + configIndex) / 2 * BaseBitsPerChannel;
baseBits[configIndex][bitOffset + 0] = false; // standby bit
baseBits[configIndex][bitOffset + referenceBit] = true;
}
Loading

0 comments on commit 60e22f1

Please sign in to comment.