Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
branches: [ main, development ]

jobs:
test:
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.14'

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
Expand All @@ -63,6 +63,9 @@ jobs:
pip install maturin pytest numpy
pip install fisher-py

- name: Decompress test data
run: gunzip -d -k test_data/*.gz

- name: Build and test
run: |
source .venv/bin/activate
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ coverage.xml
venv/
pip-log.txt
pip-delete-this-directory.txt
Pipfile
Pipfile.lock
.python-version

# Rust
target/
Expand All @@ -66,4 +69,8 @@ publish_output*.txt
*output*txt
*.dylib*
_build/
/tmp/
/tmp/
venv3.11/pyvenv.cfg
/scratch
test_data/MS2_MS1_zoom.raw

149 changes: 139 additions & 10 deletions native/ThermoNativeReader/NativeApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{
private static IRawDataPlus? _rawFile;


private static string SafeGetFilterString(IScanFilter filter)
{
if (filter == null) return "";
Expand All @@ -33,12 +34,13 @@

// Dummy usage to prevent AOT trimming of important types used by reflection in Thermo DLLs
private static ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType[] _dummyArray = new ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType[0];
private static ThermoFisher.CommonCore.Data.Interfaces.IScanFilter? _dummyFilter = null;

Check warning on line 37 in native/ThermoNativeReader/NativeApi.cs

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

The field 'NativeApi._dummyFilter' is assigned but its value is never used

static NativeApi() {
// Force compiler to keep these types
_dummyFilter = (ThermoFisher.CommonCore.Data.Interfaces.IScanFilter?)null;
var t = typeof(ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType);
var t2 = typeof(ThermoFisher.CommonCore.Data.Business.CentroidStream);
}

[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType))]
Expand Down Expand Up @@ -84,6 +86,7 @@
try
{
var scanStatistics = _rawFile.GetScanStatsForScanNumber(scanNumber);
if (scanStatistics == null) return 0;
return scanStatistics.IsCentroidScan ? 1 : 0;
}
catch
Expand Down Expand Up @@ -114,34 +117,43 @@
}
return count;
}
catch (Exception ex)

Check warning on line 120 in native/ThermoNativeReader/NativeApi.cs

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

The variable 'ex' is declared but never used
{
return -1;
}
}

[UnmanagedCallersOnly(EntryPoint = "get_centroid_stream")]
public static unsafe int GetCentroidStream(int scanNumber, double* masses, double* intensities, int maxLength)
[UnmanagedCallersOnly(EntryPoint = "get_centroid_stream_full")]
public static unsafe int GetCentroidStreamFull(int scanNumber, double* masses, double* intensities, double* baselines, double* noises, int* charges, double* noiseRes, int maxLength)
{
if (_rawFile == null) return -1;

try
{
var scan = _rawFile.GetCentroidStream(scanNumber, false);
if (scan == null) { return -2; }
if (scan.Masses == null || scan.Intensities == null) { return -3; }
if (scan == null) return 0;

int count = Math.Min(scan.Length, maxLength);
for (int i = 0; i < count; i++)
{
masses[i] = scan.Masses[i];
intensities[i] = scan.Intensities[i];
if (masses != null && scan.Masses != null && i < scan.Masses.Length) masses[i] = scan.Masses[i];
if (intensities != null && scan.Intensities != null && i < scan.Intensities.Length) intensities[i] = scan.Intensities[i];
if (baselines != null && scan.Baselines != null && i < scan.Baselines.Length) baselines[i] = scan.Baselines[i];
if (noises != null && scan.Noises != null && i < scan.Noises.Length) noises[i] = scan.Noises[i];
if (charges != null && scan.Charges != null && i < scan.Charges.Length) charges[i] = (int)scan.Charges[i];
}

if (noiseRes != null)
{
noiseRes[0] = scan.BasePeakNoise;
noiseRes[1] = scan.BasePeakResolution;
}

return count;
}
catch (Exception)
catch (Exception ex)
{
return -1;
Console.WriteLine($"Native Error in GetCentroidStreamFull: {ex.Message}");
return -1;
}
}

Expand Down Expand Up @@ -610,6 +622,25 @@
return _rawFile.SampleInformation.DilutionFactor;
}

[UnmanagedCallersOnly(EntryPoint = "get_sample_injection_volume")]
public static double GetSampleInjectionVolume()
{
if (_rawFile == null) return 0.0;
return _rawFile.SampleInformation.InjectionVolume;
}

[UnmanagedCallersOnly(EntryPoint = "get_sample_instrument_method_file")]
public static unsafe int GetSampleInstrumentMethodFile(byte* buffer, int length)
{
if (_rawFile == null) return -1;
var str = _rawFile.SampleInformation.InstrumentMethodFile ?? "";
var bytes = System.Text.Encoding.UTF8.GetBytes(str);
int count = Math.Min(bytes.Length, length - 1);
for (int i = 0; i < count; i++) buffer[i] = bytes[i];
buffer[count] = 0;
return count;
}

[UnmanagedCallersOnly(EntryPoint = "get_ms_order")]
public static int GetMsOrder(int scanNumber)
{
Expand Down Expand Up @@ -1096,7 +1127,8 @@
data[4] = stats.BasePeakMass;
data[5] = stats.BasePeakIntensity;
data[6] = stats.PacketCount;
return 7;
data[7] = stats.IsCentroidScan ? 1.0 : 0.0;
return 8;
}
catch { return -1; }
}
Expand Down Expand Up @@ -1471,5 +1503,102 @@
{
return GetFilterInt(scanNumber, "IndexToMultipleActivationIndex");
}
[UnmanagedCallersOnly(EntryPoint = "select_instrument")]
public static void SelectInstrument(int deviceType, int deviceNumber)
{
if (_rawFile == null) return;
try {
_rawFile.SelectInstrument((Device)deviceType, deviceNumber);
} catch {}
}

[UnmanagedCallersOnly(EntryPoint = "get_instrument_method_count")]
public static int GetInstrumentMethodCount()
{
if (_rawFile == null) return 0;
try {
return _rawFile.InstrumentMethodsCount;
} catch { return 0; }
}

[UnmanagedCallersOnly(EntryPoint = "get_instrument_method")]
public static unsafe int GetInstrumentMethod(int index, byte* buffer, int maxLength)
{
if (_rawFile == null) return -1;
try
{
string method = _rawFile.GetInstrumentMethod(index);
if (string.IsNullOrEmpty(method)) return 0;

byte[] bytes = System.Text.Encoding.UTF8.GetBytes(method);
int len = Math.Min(bytes.Length, maxLength - 1);
for (int i = 0; i < len; i++) buffer[i] = bytes[i];
buffer[len] = 0;
return len;
}
catch
{
return -1;
}
}
[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_index")]
public static int GetAutoSamplerTrayIndex()
{
if (_rawFile == null) return -1;
try { return _rawFile.AutoSamplerInformation.TrayIndex; }
catch (Exception ex) { Console.WriteLine($"Native Error in GetAutoSamplerTrayIndex: {ex.Message}"); return -1; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vial_index")]
public static int GetAutoSamplerVialIndex()
{
if (_rawFile == null) return -1;
try { return _rawFile.AutoSamplerInformation.VialIndex; }
catch (Exception ex) { Console.WriteLine($"Native Error in GetAutoSamplerVialIndex: {ex.Message}"); return -1; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_name")]
public static unsafe int GetAutoSamplerTrayName(byte* buffer, int maxLength)
{
if (_rawFile == null) return 0;
try
{
string name = _rawFile.AutoSamplerInformation.TrayName ?? "";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(name);
int len = Math.Min(bytes.Length, maxLength - 1);
for (int i = 0; i < len; i++) buffer[i] = bytes[i];
buffer[len] = 0;
return len;
}
catch { return 0; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_shape")]
public static int GetAutoSamplerTrayShape()
{
if (_rawFile == null) return 0;
try { return (int)_rawFile.AutoSamplerInformation.TrayShape; } catch { return 0; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray")]
public static int GetAutoSamplerVialsPerTray()
{
if (_rawFile == null) return -1;
try { return _rawFile.AutoSamplerInformation.VialsPerTray; } catch { return -1; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_x")]
public static int GetAutoSamplerVialsPerTrayX()
{
if (_rawFile == null) return -1;
try { return _rawFile.AutoSamplerInformation.VialsPerTrayX; } catch { return -1; }
}

[UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_y")]
public static int GetAutoSamplerVialsPerTrayY()
{
if (_rawFile == null) return -1;
try { return _rawFile.AutoSamplerInformation.VialsPerTrayY; } catch { return -1; }
}
}
}
2 changes: 1 addition & 1 deletion native_fisher_py/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion native_fisher_py/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "native_fisher_py"
version = "0.1.0"
version = "0.3.1"
edition = "2024"

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