Skip to content

Commit 012fe92

Browse files
authored
Merge pull request #523 from open-ephys/refactor-npx2-config
Refactor Neuropixels 2.0 probes in preparation for single-shank probes
2 parents 8e0f8cd + cace1ba commit 012fe92

26 files changed

+1014
-987
lines changed

OpenEphys.Onix1.Design/ChannelConfigurationDialog.cs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@ public partial class ChannelConfigurationDialog : Form
1919
internal event EventHandler OnResizeZedGraph;
2020
internal event EventHandler OnDrawProbeGroup;
2121

22-
internal ProbeGroup ProbeGroup;
22+
ProbeGroup probeGroup;
23+
24+
internal ProbeGroup ProbeGroup
25+
{
26+
get => probeGroup;
27+
set
28+
{
29+
probeGroup = value;
30+
SelectedContacts = new bool[probeGroup.NumberOfContacts];
31+
}
32+
}
2333

2434
internal readonly List<int> ReferenceContacts = new();
2535

26-
internal readonly bool[] SelectedContacts = null;
36+
internal bool[] SelectedContacts { get; private set; } = null;
2737

2838
[Obsolete("Designer only.", true)]
2939
ChannelConfigurationDialog()
@@ -49,8 +59,6 @@ public ChannelConfigurationDialog(ProbeGroup probeGroup)
4959
ProbeGroup = probeGroup;
5060
}
5161

52-
SelectedContacts = new bool[ProbeGroup.NumberOfContacts];
53-
5462
ReferenceContacts = new List<int>();
5563

5664
zedGraphChannels.MouseDownEvent += MouseDownEvent;
@@ -270,14 +278,26 @@ private void FormShown(object sender, EventArgs e)
270278
if (!TopLevel)
271279
{
272280
menuStrip.Visible = false;
273-
ResizeZedGraph();
274281
}
282+
283+
ResizeZedGraph();
275284
}
276285

277-
internal virtual bool OpenFile<T>() where T : ProbeGroup
286+
internal virtual bool OpenFile(Type type)
278287
{
279-
var newConfiguration = OpenAndParseConfigurationFile<T>();
288+
var newConfiguration = OpenAndParseConfigurationFile(type);
289+
290+
if (newConfiguration != null)
291+
{
292+
ProbeGroup = newConfiguration;
293+
return true;
294+
}
280295

296+
return false;
297+
}
298+
299+
internal bool ValidateProbeGroup(ProbeGroup newConfiguration)
300+
{
281301
if (newConfiguration == null)
282302
{
283303
return false;
@@ -287,10 +307,6 @@ internal virtual bool OpenFile<T>() where T : ProbeGroup
287307
{
288308
newConfiguration.Validate();
289309

290-
ProbeGroup = newConfiguration;
291-
DrawProbeGroup();
292-
RefreshZedGraph();
293-
294310
return true;
295311
}
296312
else
@@ -302,7 +318,7 @@ internal virtual bool OpenFile<T>() where T : ProbeGroup
302318
}
303319
}
304320

305-
internal T OpenAndParseConfigurationFile<T>() where T : ProbeGroup
321+
internal ProbeGroup OpenAndParseConfigurationFile(Type type)
306322
{
307323
using OpenFileDialog ofd = new();
308324

@@ -313,9 +329,19 @@ internal T OpenAndParseConfigurationFile<T>() where T : ProbeGroup
313329

314330
if (ofd.ShowDialog() == DialogResult.OK && File.Exists(ofd.FileName))
315331
{
316-
var newConfiguration = JsonHelper.DeserializeString<T>(File.ReadAllText(ofd.FileName));
332+
// NB: This method is called from an open dialog; throwing an exception without handling it would close the dialog.
333+
// Show the resulting exception as a MessageBox to warn the user of the exception with closing the whole dialog.
334+
try
335+
{
336+
var newConfiguration = ProbeInterfaceHelper.LoadExternalProbeInterfaceFile(ofd.FileName, type);
317337

318-
return newConfiguration;
338+
return ValidateProbeGroup(newConfiguration) ? newConfiguration : null;
339+
}
340+
catch (Exception ex)
341+
{
342+
MessageBox.Show(ex.Message, "Error Importing ProbeInterface File");
343+
return null;
344+
}
319345
}
320346

321347
return null;
@@ -1010,9 +1036,11 @@ void ResizeAxes()
10101036

10111037
private void MenuItemOpenFile(object sender, EventArgs e)
10121038
{
1013-
if (OpenFile<ProbeGroup>())
1039+
if (OpenFile(ProbeGroup.GetType()))
10141040
{
10151041
DrawProbeGroup();
1042+
ResetZoom();
1043+
UpdateFontSize();
10161044
RefreshZedGraph();
10171045
}
10181046
}
@@ -1021,6 +1049,7 @@ private void MenuItemLoadDefaultConfig(object sender, EventArgs e)
10211049
{
10221050
LoadDefaultChannelLayout();
10231051
DrawProbeGroup();
1052+
ResetZoom();
10241053
UpdateFontSize();
10251054
RefreshZedGraph();
10261055
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
3+
namespace OpenEphys.Onix1.Design
4+
{
5+
interface INeuropixelsV2ProbeInfo
6+
{
7+
Array GetReferenceEnumValues();
8+
9+
Array GetComboBoxChannelPresets();
10+
11+
Enum CheckForExistingChannelPreset(NeuropixelsV2Electrode[] channelMap);
12+
13+
NeuropixelsV2Electrode[] GetChannelPreset(Enum channelPreset);
14+
}
15+
}

OpenEphys.Onix1.Design/NeuropixelsV1ChannelConfigurationDialog.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,11 @@ internal override void LoadDefaultChannelLayout()
5858
OnFileOpenHandler();
5959
}
6060

61-
internal override bool OpenFile<T>()
61+
internal override bool OpenFile(Type type)
6262
{
63-
if (base.OpenFile<NeuropixelsV1eProbeGroup>())
63+
if (base.OpenFile(type))
6464
{
6565
ProbeConfiguration.ProbeGroup = (NeuropixelsV1eProbeGroup)ProbeGroup;
66-
6766
OnFileOpenHandler();
6867

6968
return true;
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Linq;
5+
6+
namespace OpenEphys.Onix1.Design
7+
{
8+
internal class NeuropixelsV2QuadShankInfo : INeuropixelsV2ProbeInfo
9+
{
10+
const int BankDStartIndex = 896;
11+
12+
enum QuadShankChannelPreset
13+
{
14+
Shank0BankA,
15+
Shank0BankB,
16+
Shank0BankC,
17+
Shank0BankD,
18+
Shank1BankA,
19+
Shank1BankB,
20+
Shank1BankC,
21+
Shank1BankD,
22+
Shank2BankA,
23+
Shank2BankB,
24+
Shank2BankC,
25+
Shank2BankD,
26+
Shank3BankA,
27+
Shank3BankB,
28+
Shank3BankC,
29+
Shank3BankD,
30+
AllShanks0_95,
31+
AllShanks96_191,
32+
AllShanks192_287,
33+
AllShanks288_383,
34+
AllShanks384_479,
35+
AllShanks480_575,
36+
AllShanks576_671,
37+
AllShanks672_767,
38+
AllShanks768_863,
39+
AllShanks864_959,
40+
AllShanks960_1055,
41+
AllShanks1056_1151,
42+
AllShanks1152_1247,
43+
None
44+
}
45+
46+
IEnumerable<NeuropixelsV2Electrode> Electrodes { get; init; }
47+
48+
public NeuropixelsV2QuadShankInfo(NeuropixelsV2QuadShankProbeConfiguration probeConfiguration)
49+
{
50+
Electrodes = probeConfiguration.ProbeGroup.ToElectrodes();
51+
}
52+
53+
public Array GetReferenceEnumValues()
54+
{
55+
return Enum.GetValues(typeof(NeuropixelsV2QuadShankReference));
56+
}
57+
58+
public Array GetComboBoxChannelPresets()
59+
{
60+
return Enum.GetValues(typeof(QuadShankChannelPreset));
61+
}
62+
63+
public Enum CheckForExistingChannelPreset(NeuropixelsV2Electrode[] channelMap)
64+
{
65+
static bool CheckShankBank(NeuropixelsV2Electrode[] channelMap, int shank, NeuropixelsV2Bank bank, out QuadShankChannelPreset preset)
66+
{
67+
preset = (shank, bank) switch
68+
{
69+
(0, NeuropixelsV2Bank.A) => QuadShankChannelPreset.Shank0BankA,
70+
(0, NeuropixelsV2Bank.B) => QuadShankChannelPreset.Shank0BankB,
71+
(0, NeuropixelsV2Bank.C) => QuadShankChannelPreset.Shank0BankC,
72+
(1, NeuropixelsV2Bank.A) => QuadShankChannelPreset.Shank1BankA,
73+
(1, NeuropixelsV2Bank.B) => QuadShankChannelPreset.Shank1BankB,
74+
(1, NeuropixelsV2Bank.C) => QuadShankChannelPreset.Shank1BankC,
75+
(2, NeuropixelsV2Bank.A) => QuadShankChannelPreset.Shank2BankA,
76+
(2, NeuropixelsV2Bank.B) => QuadShankChannelPreset.Shank2BankB,
77+
(2, NeuropixelsV2Bank.C) => QuadShankChannelPreset.Shank2BankC,
78+
(3, NeuropixelsV2Bank.A) => QuadShankChannelPreset.Shank3BankA,
79+
(3, NeuropixelsV2Bank.B) => QuadShankChannelPreset.Shank3BankB,
80+
(3, NeuropixelsV2Bank.C) => QuadShankChannelPreset.Shank3BankC,
81+
_ => QuadShankChannelPreset.None
82+
};
83+
84+
return channelMap.All(e => e.Bank == bank && e.Shank == shank);
85+
}
86+
87+
static bool CheckShankBankD(NeuropixelsV2Electrode[] channelMap, int shank, out QuadShankChannelPreset preset)
88+
{
89+
preset = shank switch
90+
{
91+
0 => QuadShankChannelPreset.Shank0BankD,
92+
1 => QuadShankChannelPreset.Shank1BankD,
93+
2 => QuadShankChannelPreset.Shank2BankD,
94+
3 => QuadShankChannelPreset.Shank3BankD,
95+
_ => QuadShankChannelPreset.None
96+
};
97+
98+
return channelMap.All(e => (e.Bank == NeuropixelsV2Bank.D || (e.Bank == NeuropixelsV2Bank.C && e.IntraShankElectrodeIndex >= BankDStartIndex)) && e.Shank == shank);
99+
}
100+
101+
for (int shank = 0; shank <= 3; shank++)
102+
{
103+
if (CheckShankBank(channelMap, shank, NeuropixelsV2Bank.A, out var preset))
104+
return preset;
105+
106+
if (CheckShankBank(channelMap, shank, NeuropixelsV2Bank.B, out preset))
107+
return preset;
108+
109+
if (CheckShankBank(channelMap, shank, NeuropixelsV2Bank.C, out preset))
110+
return preset;
111+
112+
if (CheckShankBankD(channelMap, shank, out preset))
113+
return preset;
114+
}
115+
116+
var ranges = new[]
117+
{
118+
(0, 95, QuadShankChannelPreset.AllShanks0_95),
119+
(192, 287, QuadShankChannelPreset.AllShanks192_287),
120+
(288, 383, QuadShankChannelPreset.AllShanks288_383),
121+
(394, 479, QuadShankChannelPreset.AllShanks384_479),
122+
(480, 575, QuadShankChannelPreset.AllShanks480_575),
123+
(576, 671, QuadShankChannelPreset.AllShanks576_671),
124+
(672, 767, QuadShankChannelPreset.AllShanks672_767),
125+
(768, 863, QuadShankChannelPreset.AllShanks768_863),
126+
(864, 959, QuadShankChannelPreset.AllShanks864_959),
127+
(960, 1055, QuadShankChannelPreset.AllShanks960_1055),
128+
(1056, 1151, QuadShankChannelPreset.AllShanks1056_1151),
129+
(1152, 1247, QuadShankChannelPreset.AllShanks1152_1247)
130+
};
131+
132+
static bool CheckAllShanksRange(NeuropixelsV2Electrode[] channelMap, int start, int end)
133+
{
134+
return channelMap.All(e => e.Shank >= 0 && e.Shank <= 3 &&
135+
e.IntraShankElectrodeIndex >= start &&
136+
e.IntraShankElectrodeIndex <= end);
137+
}
138+
139+
foreach (var (start, end, presetValue) in ranges)
140+
{
141+
if (CheckAllShanksRange(channelMap, start, end))
142+
return presetValue;
143+
}
144+
145+
return QuadShankChannelPreset.None;
146+
}
147+
148+
public NeuropixelsV2Electrode[] GetChannelPreset(Enum channelPreset)
149+
{
150+
var preset = (QuadShankChannelPreset)channelPreset;
151+
152+
static NeuropixelsV2Electrode[] GetAllShanks(IEnumerable<NeuropixelsV2Electrode> electrodes, int startIndex, int endIndex)
153+
{
154+
return electrodes.Where(e => (e.Shank == 0 && e.IntraShankElectrodeIndex >= startIndex && e.IntraShankElectrodeIndex <= endIndex)
155+
|| (e.Shank == 1 && e.IntraShankElectrodeIndex >= startIndex && e.IntraShankElectrodeIndex <= endIndex)
156+
|| (e.Shank == 2 && e.IntraShankElectrodeIndex >= startIndex && e.IntraShankElectrodeIndex <= endIndex)
157+
|| (e.Shank == 3 && e.IntraShankElectrodeIndex >= startIndex && e.IntraShankElectrodeIndex <= endIndex)).ToArray();
158+
};
159+
160+
return preset switch
161+
{
162+
QuadShankChannelPreset.Shank0BankA => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 0).ToArray(),
163+
QuadShankChannelPreset.Shank0BankB => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 0).ToArray(),
164+
QuadShankChannelPreset.Shank0BankC => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 0).ToArray(),
165+
QuadShankChannelPreset.Shank0BankD => Electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 0).ToArray(),
166+
QuadShankChannelPreset.Shank1BankA => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 1).ToArray(),
167+
QuadShankChannelPreset.Shank1BankB => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 1).ToArray(),
168+
QuadShankChannelPreset.Shank1BankC => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 1).ToArray(),
169+
QuadShankChannelPreset.Shank1BankD => Electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 1).ToArray(),
170+
QuadShankChannelPreset.Shank2BankA => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 2).ToArray(),
171+
QuadShankChannelPreset.Shank2BankB => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 2).ToArray(),
172+
QuadShankChannelPreset.Shank2BankC => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 2).ToArray(),
173+
QuadShankChannelPreset.Shank2BankD => Electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 2).ToArray(),
174+
QuadShankChannelPreset.Shank3BankA => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.A && e.Shank == 3).ToArray(),
175+
QuadShankChannelPreset.Shank3BankB => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.B && e.Shank == 3).ToArray(),
176+
QuadShankChannelPreset.Shank3BankC => Electrodes.Where(e => e.Bank == NeuropixelsV2Bank.C && e.Shank == 3).ToArray(),
177+
QuadShankChannelPreset.Shank3BankD => Electrodes.Where(e => (e.Bank == NeuropixelsV2Bank.D || (e.Bank == NeuropixelsV2Bank.C && e.Index >= BankDStartIndex)) && e.Shank == 3).ToArray(),
178+
QuadShankChannelPreset.AllShanks0_95 => GetAllShanks(Electrodes, 0, 95),
179+
QuadShankChannelPreset.AllShanks96_191 => GetAllShanks(Electrodes, 96, 191),
180+
QuadShankChannelPreset.AllShanks192_287 => GetAllShanks(Electrodes, 192, 287),
181+
QuadShankChannelPreset.AllShanks288_383 => GetAllShanks(Electrodes, 288, 383),
182+
QuadShankChannelPreset.AllShanks384_479 => GetAllShanks(Electrodes, 384, 479),
183+
QuadShankChannelPreset.AllShanks480_575 => GetAllShanks(Electrodes, 480, 575),
184+
QuadShankChannelPreset.AllShanks576_671 => GetAllShanks(Electrodes, 576, 671),
185+
QuadShankChannelPreset.AllShanks672_767 => GetAllShanks(Electrodes, 672, 767),
186+
QuadShankChannelPreset.AllShanks768_863 => GetAllShanks(Electrodes, 768, 863),
187+
QuadShankChannelPreset.AllShanks864_959 => GetAllShanks(Electrodes, 864, 959),
188+
QuadShankChannelPreset.AllShanks960_1055 => GetAllShanks(Electrodes, 960, 1055),
189+
QuadShankChannelPreset.AllShanks1056_1151 => GetAllShanks(Electrodes, 1056, 1151),
190+
QuadShankChannelPreset.AllShanks1152_1247 => GetAllShanks(Electrodes, 1152, 1247),
191+
QuadShankChannelPreset.None => Array.Empty<NeuropixelsV2Electrode>(),
192+
_ => throw new InvalidEnumArgumentException($"Unknown value of {nameof(QuadShankChannelPreset)}: {channelPreset}")
193+
};
194+
}
195+
}
196+
}

0 commit comments

Comments
 (0)