Skip to content

Commit 21ea1db

Browse files
authored
Merge pull request #2 from z3rone-org/feature/centroid-stream
feat: implement centroid streaming and ignore large raw data
2 parents 5b4eb95 + d152280 commit 21ea1db

16 files changed

Lines changed: 695 additions & 195 deletions

File tree

.github/workflows/test.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on:
44
push:
55
branches: [ main ]
66
pull_request:
7-
branches: [ main ]
7+
branches: [ main, development ]
88

99
jobs:
1010
test:
@@ -51,7 +51,7 @@ jobs:
5151
- name: Set up Python
5252
uses: actions/setup-python@v5
5353
with:
54-
python-version: '3.9'
54+
python-version: '3.14'
5555

5656
- name: Install Rust
5757
uses: dtolnay/rust-toolchain@stable
@@ -63,6 +63,9 @@ jobs:
6363
pip install maturin pytest numpy
6464
pip install fisher-py
6565
66+
- name: Decompress test data
67+
run: gunzip -d -k test_data/*.gz
68+
6669
- name: Build and test
6770
run: |
6871
source .venv/bin/activate

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ coverage.xml
5454
venv/
5555
pip-log.txt
5656
pip-delete-this-directory.txt
57+
Pipfile
58+
Pipfile.lock
59+
.python-version
5760

5861
# Rust
5962
target/
@@ -66,4 +69,8 @@ publish_output*.txt
6669
*output*txt
6770
*.dylib*
6871
_build/
69-
/tmp/
72+
/tmp/
73+
venv3.11/pyvenv.cfg
74+
/scratch
75+
test_data/MS2_MS1_zoom.raw
76+

native/ThermoNativeReader/NativeApi.cs

Lines changed: 139 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public static class NativeApi
1717
{
1818
private static IRawDataPlus? _rawFile;
1919

20+
2021
private static string SafeGetFilterString(IScanFilter filter)
2122
{
2223
if (filter == null) return "";
@@ -39,6 +40,7 @@ static NativeApi() {
3940
// Force compiler to keep these types
4041
_dummyFilter = (ThermoFisher.CommonCore.Data.Interfaces.IScanFilter?)null;
4142
var t = typeof(ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType);
43+
var t2 = typeof(ThermoFisher.CommonCore.Data.Business.CentroidStream);
4244
}
4345

4446
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType))]
@@ -84,6 +86,7 @@ public static int IsCentroid(int scanNumber)
8486
try
8587
{
8688
var scanStatistics = _rawFile.GetScanStatsForScanNumber(scanNumber);
89+
if (scanStatistics == null) return 0;
8790
return scanStatistics.IsCentroidScan ? 1 : 0;
8891
}
8992
catch
@@ -120,28 +123,37 @@ public static unsafe int GetSpectrum(int scanNumber, double* masses, double* int
120123
}
121124
}
122125

123-
[UnmanagedCallersOnly(EntryPoint = "get_centroid_stream")]
124-
public static unsafe int GetCentroidStream(int scanNumber, double* masses, double* intensities, int maxLength)
126+
[UnmanagedCallersOnly(EntryPoint = "get_centroid_stream_full")]
127+
public static unsafe int GetCentroidStreamFull(int scanNumber, double* masses, double* intensities, double* baselines, double* noises, int* charges, double* noiseRes, int maxLength)
125128
{
126129
if (_rawFile == null) return -1;
127-
128130
try
129131
{
130132
var scan = _rawFile.GetCentroidStream(scanNumber, false);
131-
if (scan == null) { return -2; }
132-
if (scan.Masses == null || scan.Intensities == null) { return -3; }
133+
if (scan == null) return 0;
133134

134135
int count = Math.Min(scan.Length, maxLength);
135136
for (int i = 0; i < count; i++)
136137
{
137-
masses[i] = scan.Masses[i];
138-
intensities[i] = scan.Intensities[i];
138+
if (masses != null && scan.Masses != null && i < scan.Masses.Length) masses[i] = scan.Masses[i];
139+
if (intensities != null && scan.Intensities != null && i < scan.Intensities.Length) intensities[i] = scan.Intensities[i];
140+
if (baselines != null && scan.Baselines != null && i < scan.Baselines.Length) baselines[i] = scan.Baselines[i];
141+
if (noises != null && scan.Noises != null && i < scan.Noises.Length) noises[i] = scan.Noises[i];
142+
if (charges != null && scan.Charges != null && i < scan.Charges.Length) charges[i] = (int)scan.Charges[i];
143+
}
144+
145+
if (noiseRes != null)
146+
{
147+
noiseRes[0] = scan.BasePeakNoise;
148+
noiseRes[1] = scan.BasePeakResolution;
139149
}
150+
140151
return count;
141152
}
142-
catch (Exception)
153+
catch (Exception ex)
143154
{
144-
return -1;
155+
Console.WriteLine($"Native Error in GetCentroidStreamFull: {ex.Message}");
156+
return -1;
145157
}
146158
}
147159

@@ -610,6 +622,25 @@ public static double GetSampleDilutionFactor()
610622
return _rawFile.SampleInformation.DilutionFactor;
611623
}
612624

625+
[UnmanagedCallersOnly(EntryPoint = "get_sample_injection_volume")]
626+
public static double GetSampleInjectionVolume()
627+
{
628+
if (_rawFile == null) return 0.0;
629+
return _rawFile.SampleInformation.InjectionVolume;
630+
}
631+
632+
[UnmanagedCallersOnly(EntryPoint = "get_sample_instrument_method_file")]
633+
public static unsafe int GetSampleInstrumentMethodFile(byte* buffer, int length)
634+
{
635+
if (_rawFile == null) return -1;
636+
var str = _rawFile.SampleInformation.InstrumentMethodFile ?? "";
637+
var bytes = System.Text.Encoding.UTF8.GetBytes(str);
638+
int count = Math.Min(bytes.Length, length - 1);
639+
for (int i = 0; i < count; i++) buffer[i] = bytes[i];
640+
buffer[count] = 0;
641+
return count;
642+
}
643+
613644
[UnmanagedCallersOnly(EntryPoint = "get_ms_order")]
614645
public static int GetMsOrder(int scanNumber)
615646
{
@@ -1096,7 +1127,8 @@ public static unsafe int GetScanStats(int scanNumber, double* data)
10961127
data[4] = stats.BasePeakMass;
10971128
data[5] = stats.BasePeakIntensity;
10981129
data[6] = stats.PacketCount;
1099-
return 7;
1130+
data[7] = stats.IsCentroidScan ? 1.0 : 0.0;
1131+
return 8;
11001132
}
11011133
catch { return -1; }
11021134
}
@@ -1471,5 +1503,102 @@ public static int GetScanFilterIndexToMultipleActivationIndex(int scanNumber)
14711503
{
14721504
return GetFilterInt(scanNumber, "IndexToMultipleActivationIndex");
14731505
}
1506+
[UnmanagedCallersOnly(EntryPoint = "select_instrument")]
1507+
public static void SelectInstrument(int deviceType, int deviceNumber)
1508+
{
1509+
if (_rawFile == null) return;
1510+
try {
1511+
_rawFile.SelectInstrument((Device)deviceType, deviceNumber);
1512+
} catch {}
1513+
}
1514+
1515+
[UnmanagedCallersOnly(EntryPoint = "get_instrument_method_count")]
1516+
public static int GetInstrumentMethodCount()
1517+
{
1518+
if (_rawFile == null) return 0;
1519+
try {
1520+
return _rawFile.InstrumentMethodsCount;
1521+
} catch { return 0; }
1522+
}
1523+
1524+
[UnmanagedCallersOnly(EntryPoint = "get_instrument_method")]
1525+
public static unsafe int GetInstrumentMethod(int index, byte* buffer, int maxLength)
1526+
{
1527+
if (_rawFile == null) return -1;
1528+
try
1529+
{
1530+
string method = _rawFile.GetInstrumentMethod(index);
1531+
if (string.IsNullOrEmpty(method)) return 0;
1532+
1533+
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(method);
1534+
int len = Math.Min(bytes.Length, maxLength - 1);
1535+
for (int i = 0; i < len; i++) buffer[i] = bytes[i];
1536+
buffer[len] = 0;
1537+
return len;
1538+
}
1539+
catch
1540+
{
1541+
return -1;
1542+
}
1543+
}
1544+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_index")]
1545+
public static int GetAutoSamplerTrayIndex()
1546+
{
1547+
if (_rawFile == null) return -1;
1548+
try { return _rawFile.AutoSamplerInformation.TrayIndex; }
1549+
catch (Exception ex) { Console.WriteLine($"Native Error in GetAutoSamplerTrayIndex: {ex.Message}"); return -1; }
1550+
}
1551+
1552+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vial_index")]
1553+
public static int GetAutoSamplerVialIndex()
1554+
{
1555+
if (_rawFile == null) return -1;
1556+
try { return _rawFile.AutoSamplerInformation.VialIndex; }
1557+
catch (Exception ex) { Console.WriteLine($"Native Error in GetAutoSamplerVialIndex: {ex.Message}"); return -1; }
1558+
}
1559+
1560+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_name")]
1561+
public static unsafe int GetAutoSamplerTrayName(byte* buffer, int maxLength)
1562+
{
1563+
if (_rawFile == null) return 0;
1564+
try
1565+
{
1566+
string name = _rawFile.AutoSamplerInformation.TrayName ?? "";
1567+
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(name);
1568+
int len = Math.Min(bytes.Length, maxLength - 1);
1569+
for (int i = 0; i < len; i++) buffer[i] = bytes[i];
1570+
buffer[len] = 0;
1571+
return len;
1572+
}
1573+
catch { return 0; }
1574+
}
1575+
1576+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_shape")]
1577+
public static int GetAutoSamplerTrayShape()
1578+
{
1579+
if (_rawFile == null) return 0;
1580+
try { return (int)_rawFile.AutoSamplerInformation.TrayShape; } catch { return 0; }
1581+
}
1582+
1583+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray")]
1584+
public static int GetAutoSamplerVialsPerTray()
1585+
{
1586+
if (_rawFile == null) return -1;
1587+
try { return _rawFile.AutoSamplerInformation.VialsPerTray; } catch { return -1; }
1588+
}
1589+
1590+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_x")]
1591+
public static int GetAutoSamplerVialsPerTrayX()
1592+
{
1593+
if (_rawFile == null) return -1;
1594+
try { return _rawFile.AutoSamplerInformation.VialsPerTrayX; } catch { return -1; }
1595+
}
1596+
1597+
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_y")]
1598+
public static int GetAutoSamplerVialsPerTrayY()
1599+
{
1600+
if (_rawFile == null) return -1;
1601+
try { return _rawFile.AutoSamplerInformation.VialsPerTrayY; } catch { return -1; }
1602+
}
14741603
}
14751604
}

native_fisher_py/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

native_fisher_py/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "native_fisher_py"
3-
version = "0.1.0"
3+
version = "0.3.1"
44
edition = "2024"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

0 commit comments

Comments
 (0)