diff --git a/.github/patch_fisher_py.py b/.github/patch_fisher_py.py index 3caead8..ea5e271 100644 --- a/.github/patch_fisher_py.py +++ b/.github/patch_fisher_py.py @@ -25,15 +25,25 @@ def patch_fisher_py(): with open(init_file, 'r') as f: content = f.read() - # Add import System.Reflection if needed - if "from System.Reflection import Assembly" not in content: - content = content.replace("from System import Environment", "from System import Environment\nfrom System.Reflection import Assembly") + # Add import sys if needed + if "import sys" not in content: + content = "import sys\n" + content - # Replace clr.AddReference(os.path.join(dll_path, '...')) - # with Assembly.LoadFrom(os.path.realpath(os.path.join(dll_path, '...'))) + # Ensure sys.path.append(dll_path) is added + if "sys.path.append(os.path.realpath(dll_path))" not in content: + content = content.replace("clr.AddReference('mscorlib')", "clr.AddReference('mscorlib')\nsys.path.append(os.path.realpath(dll_path))") + + # Replace clr.AddReference(os.path.join(dll_path, 'AssemblyName.dll')) + # with clr.AddReference('AssemblyName') + content = re.sub( + r"clr\.AddReference\((?:os\.path\.join\()?dll_path,\s*'([^']+)\.dll'(?:\))?\)", + r"clr.AddReference('\1')", + content + ) + # Also clean up any lingering Assembly.LoadFrom from previous patches just in case content = re.sub( - r"clr\.AddReference\((os\.path\.join\(dll_path, '[^']+'\))\)", - r"Assembly.LoadFrom(os.path.realpath(\1))", + r"Assembly\.LoadFrom\(os\.path\.realpath\(os\.path\.join\(dll_path,\s*'([^']+)\.dll'\)\)\)", + r"clr.AddReference('\1')", content ) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fa5ac62..ffaf7b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] + branches: [ main, development ] jobs: test: @@ -51,7 +51,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -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 @@ -72,7 +75,10 @@ jobs: cp ${{ env.DYLIB_PATH }} native_fisher_py/python/native_fisher_py/ # Install the package - pip install ./native_fisher_py + cp README.md native_fisher_py/ + cd native_fisher_py + maturin develop + cd .. # Parity tests will automatically fall back to ground_truth.json if fisher-py fails python -m pytest tests diff --git a/.gitignore b/.gitignore index ee06987..6a285bd 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,9 @@ coverage.xml venv/ pip-log.txt pip-delete-this-directory.txt +Pipfile +Pipfile.lock +.python-version # Rust target/ @@ -68,3 +71,7 @@ publish_output*.txt _build/ /tmp/ venv3.11/pyvenv.cfg +/scratch + +# Downloadable test data +test_data/*.raw \ No newline at end of file diff --git a/build.sh b/build.sh index 34fed1f..2368926 100755 --- a/build.sh +++ b/build.sh @@ -28,12 +28,14 @@ cd ../.. # Copy the built library into the python package directory # This allows maturin to include it in the wheel cp $DYLIB_PATH native_fisher_py/python/native_fisher_py/ +cp vendor/RawFileReader/Libs/NetCore/Net8/Assemblies/*.dll native_fisher_py/python/native_fisher_py/ # 2. Build Python Package cd native_fisher_py export THERMO_NATIVE_LIB=$(pwd)/python/native_fisher_py/$LIB_NAME # Check for maturin +cp ../README.md README.md if command -v maturin >/dev/null 2>&1; then maturin develop elif command -v pipenv >/dev/null 2>&1 && pipenv run maturin --version >/dev/null 2>&1; then diff --git a/native/ThermoNativeReader/NativeApi.cs b/native/ThermoNativeReader/NativeApi.cs index 7f4edff..e8cf83c 100644 --- a/native/ThermoNativeReader/NativeApi.cs +++ b/native/ThermoNativeReader/NativeApi.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; @@ -15,7 +16,29 @@ namespace ThermoNativeReader { public static class NativeApi { - private static IRawDataPlus? _rawFile; + class FileState + { + public IRawDataPlus RawFile; + public int CachedMethodCount = 0; + public int CachedSampleType = 0; + public int CachedSampleRow = 0; + public double CachedSampleDilution = 0.0; + } + + private static ConcurrentDictionary _files = new ConcurrentDictionary(); + private static int _nextHandle = 1; + + private static IRawDataPlus GetFile(int handle) + { + if (_files.TryGetValue(handle, out var state)) return state.RawFile; + return null; + } + private static FileState GetState(int handle) + { + if (_files.TryGetValue(handle, out var state)) return state; + return null; + } + private static string SafeGetFilterString(IScanFilter filter) { @@ -39,6 +62,7 @@ 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))] @@ -49,52 +73,66 @@ public static unsafe int OpenRawFile(byte* pathPtr) try { if (pathPtr == null) return -1; - string? path = Marshal.PtrToStringAnsi((IntPtr)pathPtr); + string path = System.Runtime.InteropServices.Marshal.PtrToStringUTF8((IntPtr)pathPtr); if (string.IsNullOrEmpty(path)) return -1; - _rawFile = (IRawDataPlus)RawFileReaderAdapter.FileFactory(path); - if (_rawFile == null) return -1; - _rawFile.SelectInstrument(Device.MS, 1); - return 0; + var rawFile = (IRawDataPlus)RawFileReaderAdapter.FileFactory(path); + if (rawFile == null) return -1; + + var state = new FileState { RawFile = rawFile }; + + try { state.CachedMethodCount = rawFile.InstrumentMethodsCount; } catch { state.CachedMethodCount = 0; } + try { state.CachedSampleType = (int)rawFile.SampleInformation.SampleType; } catch { state.CachedSampleType = 0; } + try { state.CachedSampleRow = rawFile.SampleInformation.RowNumber; } catch { state.CachedSampleRow = 0; } + try { state.CachedSampleDilution = rawFile.SampleInformation.DilutionFactor; } catch { state.CachedSampleDilution = 0.0; } + + rawFile.SelectInstrument(Device.MS, 1); + + int handle = System.Threading.Interlocked.Increment(ref _nextHandle); + _files[handle] = state; + return handle; } - catch + catch (Exception ex) { + Console.Error.WriteLine("[native-fisher-py] Exception in OpenRawFile: " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_num_scans")] - public static int GetNumScans() + public static int GetNumScans(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1; return _rawFile.RunHeader.LastSpectrum; } [UnmanagedCallersOnly(EntryPoint = "get_scan_rt")] - public static double GetScanRT(int scanNumber) + public static double GetScanRT(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1.0; return _rawFile.RetentionTimeFromScanNumber(scanNumber); } [UnmanagedCallersOnly(EntryPoint = "is_centroid")] - public static int IsCentroid(int scanNumber) + public static int IsCentroid(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; try { var scanStatistics = _rawFile.GetScanStatsForScanNumber(scanNumber); + if (scanStatistics == null) return 0; return scanStatistics.IsCentroidScan ? 1 : 0; } - catch - { - return 0; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in IsCentroid (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_spectrum")] - public static unsafe int GetSpectrum(int scanNumber, double* masses, double* intensities, int maxLength) + public static unsafe int GetSpectrum(int handle, int scanNumber, double* masses, double* intensities, int maxLength) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try @@ -120,36 +158,47 @@ public static unsafe int GetSpectrum(int scanNumber, double* masses, double* int } } - [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 handle, int scanNumber, double* masses, double* intensities, double* baselines, double* noises, int* charges, double* noiseRes, int maxLength) { + var _rawFile = GetFile(handle); 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; } } [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ThermoFisher.CommonCore.Data.Interfaces.MetaFilterType))] [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ThermoFisher.CommonCore.Data.Interfaces.IScanFilter))] [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_meta_filters")] - public static unsafe int GetScanFilterMetaFilters(int scanNumber, IntPtr* filters, int maxCount) + public static unsafe int GetScanFilterMetaFilters(int handle, int scanNumber, IntPtr* filters, int maxCount) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -172,12 +221,13 @@ public static unsafe int GetScanFilterMetaFilters(int scanNumber, IntPtr* filter } return metaList.Count; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in IsCentroid (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_filters")] - public static unsafe int GetFilters(IntPtr* filters, int maxCount) + public static unsafe int GetFilters(int handle, IntPtr* filters, int maxCount) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -197,78 +247,89 @@ public static unsafe int GetFilters(IntPtr* filters, int maxCount) } [UnmanagedCallersOnly(EntryPoint = "get_first_scan")] - public static int GetFirstScan() + public static int GetFirstScan(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1; return _rawFile.RunHeader.FirstSpectrum; } [UnmanagedCallersOnly(EntryPoint = "get_last_scan")] - public static int GetLastScan() + public static int GetLastScan(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1; return _rawFile.RunHeader.LastSpectrum; } [UnmanagedCallersOnly(EntryPoint = "get_end_time")] - public static double GetEndTime() + public static double GetEndTime(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.EndTime; } [UnmanagedCallersOnly(EntryPoint = "get_start_time")] - public static double GetStartTime() + public static double GetStartTime(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.StartTime; } [UnmanagedCallersOnly(EntryPoint = "get_mass_resolution")] - public static double GetMassResolution() + public static double GetMassResolution(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.MassResolution; } [UnmanagedCallersOnly(EntryPoint = "get_expected_runtime")] - public static double GetExpectedRuntime() + public static double GetExpectedRuntime(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.ExpectedRuntime; } [UnmanagedCallersOnly(EntryPoint = "get_max_integrated_intensity")] - public static double GetMaxIntegratedIntensity() + public static double GetMaxIntegratedIntensity(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.MaxIntegratedIntensity; } [UnmanagedCallersOnly(EntryPoint = "get_max_intensity")] - public static int GetMaxIntensity() + public static int GetMaxIntensity(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1; return _rawFile.RunHeader.MaxIntensity; } [UnmanagedCallersOnly(EntryPoint = "get_low_mass")] - public static double GetLowMass() + public static double GetLowMass(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.LowMass; } [UnmanagedCallersOnly(EntryPoint = "get_high_mass")] - public static double GetHighMass() + public static double GetHighMass(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null || _rawFile.RunHeader == null) return -1.0; return _rawFile.RunHeader.HighMass; } [UnmanagedCallersOnly(EntryPoint = "get_file_name")] - public static unsafe int GetFileName(byte* buffer, int length) + public static unsafe int GetFileName(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileName ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -279,8 +340,9 @@ public static unsafe int GetFileName(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_path")] - public static unsafe int GetPath(byte* buffer, int length) + public static unsafe int GetPath(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.Path ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -291,15 +353,17 @@ public static unsafe int GetPath(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_tune_data_count")] - public static int GetTuneDataCount() + public static int GetTuneDataCount(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; return _rawFile.GetTuneDataCount(); } [UnmanagedCallersOnly(EntryPoint = "get_creation_date")] - public static unsafe int GetCreationDate(byte* buffer, int length) + public static unsafe int GetCreationDate(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.CreationDate.ToString("o"); var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -310,8 +374,9 @@ public static unsafe int GetCreationDate(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_computer_name")] - public static unsafe int GetComputerName(byte* buffer, int length) + public static unsafe int GetComputerName(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.ComputerName ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -322,8 +387,9 @@ public static unsafe int GetComputerName(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_creator_id")] - public static unsafe int GetCreatorID(byte* buffer, int length) + public static unsafe int GetCreatorID(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.CreatorId ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -334,8 +400,9 @@ public static unsafe int GetCreatorID(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_model")] - public static unsafe int GetInstrumentModel(byte* buffer, int length) + public static unsafe int GetInstrumentModel(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.Model ?? ""; @@ -347,8 +414,9 @@ public static unsafe int GetInstrumentModel(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_name")] - public static unsafe int GetInstrumentName(byte* buffer, int length) + public static unsafe int GetInstrumentName(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.Name ?? ""; @@ -360,8 +428,9 @@ public static unsafe int GetInstrumentName(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_serial_number")] - public static unsafe int GetInstrumentSerialNumber(byte* buffer, int length) + public static unsafe int GetInstrumentSerialNumber(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.SerialNumber ?? ""; @@ -373,8 +442,9 @@ public static unsafe int GetInstrumentSerialNumber(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_software_version")] - public static unsafe int GetInstrumentSoftwareVersion(byte* buffer, int length) + public static unsafe int GetInstrumentSoftwareVersion(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.SoftwareVersion ?? ""; @@ -386,8 +456,9 @@ public static unsafe int GetInstrumentSoftwareVersion(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_hardware_version")] - public static unsafe int GetInstrumentHardwareVersion(byte* buffer, int length) + public static unsafe int GetInstrumentHardwareVersion(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.HardwareVersion ?? ""; @@ -399,8 +470,9 @@ public static unsafe int GetInstrumentHardwareVersion(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_axis_label_x")] - public static unsafe int GetInstrumentAxisLabelX(byte* buffer, int length) + public static unsafe int GetInstrumentAxisLabelX(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.AxisLabelX ?? ""; @@ -412,8 +484,9 @@ public static unsafe int GetInstrumentAxisLabelX(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_axis_label_y")] - public static unsafe int GetInstrumentAxisLabelY(byte* buffer, int length) + public static unsafe int GetInstrumentAxisLabelY(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.AxisLabelY ?? ""; @@ -425,8 +498,9 @@ public static unsafe int GetInstrumentAxisLabelY(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_flags")] - public static unsafe int GetInstrumentFlags(byte* buffer, int length) + public static unsafe int GetInstrumentFlags(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); var str = data?.Flags ?? ""; @@ -438,40 +512,45 @@ public static unsafe int GetInstrumentFlags(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_instrument_units")] - public static int GetInstrumentUnits() + public static int GetInstrumentUnits(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); return data != null ? (int)data.Units : 0; } [UnmanagedCallersOnly(EntryPoint = "get_instrument_is_valid")] - public static int GetInstrumentIsValid() + public static int GetInstrumentIsValid(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); return data != null && data.IsValid ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "get_instrument_has_accurate_mass_precursors")] - public static int GetInstrumentHasAccurateMassPrecursors() + public static int GetInstrumentHasAccurateMassPrecursors(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); return data != null && data.HasAccurateMassPrecursors ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "get_instrument_is_tsq_quantum_file")] - public static int GetInstrumentIsTsqQuantumFile() + public static int GetInstrumentIsTsqQuantumFile(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var data = _rawFile.GetInstrumentData(); return data != null && data.IsTsqQuantumFile() ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "get_file_description")] - public static unsafe int GetFileDescription(byte* buffer, int length) + public static unsafe int GetFileDescription(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileHeader.FileDescription ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -482,8 +561,9 @@ public static unsafe int GetFileDescription(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_modified_date")] - public static unsafe int GetModifiedDate(byte* buffer, int length) + public static unsafe int GetModifiedDate(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileHeader.ModifiedDate.ToString() ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -494,8 +574,9 @@ public static unsafe int GetModifiedDate(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_who_created_logon")] - public static unsafe int GetWhoCreatedLogon(byte* buffer, int length) + public static unsafe int GetWhoCreatedLogon(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileHeader.WhoCreatedLogon ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -506,8 +587,9 @@ public static unsafe int GetWhoCreatedLogon(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_who_modified_id")] - public static unsafe int GetWhoModifiedId(byte* buffer, int length) + public static unsafe int GetWhoModifiedId(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileHeader.WhoModifiedId ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -518,8 +600,9 @@ public static unsafe int GetWhoModifiedId(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_who_modified_logon")] - public static unsafe int GetWhoModifiedLogon(byte* buffer, int length) + public static unsafe int GetWhoModifiedLogon(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.FileHeader.WhoModifiedLogon ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -530,8 +613,9 @@ public static unsafe int GetWhoModifiedLogon(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_barcode")] - public static unsafe int GetSampleBarcode(byte* buffer, int length) + public static unsafe int GetSampleBarcode(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.SampleInformation.Barcode ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -542,8 +626,9 @@ public static unsafe int GetSampleBarcode(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_id")] - public static unsafe int GetSampleId(byte* buffer, int length) + public static unsafe int GetSampleId(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.SampleInformation.SampleId ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -554,8 +639,9 @@ public static unsafe int GetSampleId(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_name")] - public static unsafe int GetSampleName(byte* buffer, int length) + public static unsafe int GetSampleName(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.SampleInformation.SampleName ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -566,8 +652,9 @@ public static unsafe int GetSampleName(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_vial")] - public static unsafe int GetSampleVial(byte* buffer, int length) + public static unsafe int GetSampleVial(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.SampleInformation.Vial ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -578,8 +665,9 @@ public static unsafe int GetSampleVial(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_comment")] - public static unsafe int GetSampleComment(byte* buffer, int length) + public static unsafe int GetSampleComment(int handle, byte* buffer, int length) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; var str = _rawFile.SampleInformation.Comment ?? ""; var bytes = System.Text.Encoding.UTF8.GetBytes(str); @@ -590,59 +678,80 @@ public static unsafe int GetSampleComment(byte* buffer, int length) } [UnmanagedCallersOnly(EntryPoint = "get_sample_type")] - public static int GetSampleType() + public static int GetSampleType(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; return (int)_rawFile.SampleInformation.SampleType; } [UnmanagedCallersOnly(EntryPoint = "get_sample_row_number")] - public static int GetSampleRowNumber() + public static int GetSampleRowNumber(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; return _rawFile.SampleInformation.RowNumber; } [UnmanagedCallersOnly(EntryPoint = "get_sample_dilution_factor")] - public static double GetSampleDilutionFactor() + public static double GetSampleDilutionFactor(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 1.0; return _rawFile.SampleInformation.DilutionFactor; } + [UnmanagedCallersOnly(EntryPoint = "get_sample_injection_volume")] + public static double GetSampleInjectionVolume(int handle) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return 0.0; + return _rawFile.SampleInformation.InjectionVolume; + } + + [UnmanagedCallersOnly(EntryPoint = "get_sample_instrument_method_file")] + public static unsafe int GetSampleInstrumentMethodFile(int handle, byte* buffer, int length) + { + var _rawFile = GetFile(handle); + 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) + public static int GetMsOrder(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var scanEvent = _rawFile.GetScanEventForScanNumber(scanNumber); return (int)scanEvent.MSOrder; } - catch - { - return -1; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetMsOrder (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_mass_analyzer")] - public static int GetMassAnalyzer(int scanNumber) + public static int GetMassAnalyzer(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var scanEvent = _rawFile.GetScanEventForScanNumber(scanNumber); return (int)scanEvent.MassAnalyzer; } - catch - { - return -1; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetMassAnalyzer (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_precursor_mass")] - public static double GetPrecursorMass(int scanNumber) + public static double GetPrecursorMass(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1.0; try { @@ -650,10 +759,7 @@ public static double GetPrecursorMass(int scanNumber) if (scanEvent.MSOrder == MSOrderType.Ms) return 0.0; return scanEvent.GetReaction(0).PrecursorMass; } - catch - { - return -1.0; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetPrecursorMass (fallback -1.0): " + ex.Message); return -1.0; } } private static string SafeGetScanEventString(IScanEvent scanEvent) @@ -676,8 +782,9 @@ private static string SafeGetScanEventString(IScanEvent scanEvent) } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_string")] - public static unsafe int GetScanEventString(int scanNumber, byte* buffer, int bufferSize) + public static unsafe int GetScanEventString(int handle, int scanNumber, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; try { @@ -705,8 +812,9 @@ public static unsafe int GetScanEventString(int scanNumber, byte* buffer, int bu } [UnmanagedCallersOnly(EntryPoint = "get_ms2_filter_masses")] - public static unsafe int GetMs2FilterMasses(double* buffer, int maxSize) + public static unsafe int GetMs2FilterMasses(int handle, double* buffer, int maxSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -736,8 +844,9 @@ public static unsafe int GetMs2FilterMasses(double* buffer, int maxSize) } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_string")] - public static unsafe int GetScanFilterString(int scanNumber, byte* buffer, int bufferSize) + public static unsafe int GetScanFilterString(int handle, int scanNumber, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; try { @@ -764,15 +873,17 @@ public static unsafe int GetScanFilterString(int scanNumber, byte* buffer, int b } [UnmanagedCallersOnly(EntryPoint = "get_scan_number_from_rt")] - public static int GetScanNumberFromRT(double rt) + public static int GetScanNumberFromRT(int handle, double rt) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; return _rawFile.ScanNumberFromRetentionTime(rt); } [UnmanagedCallersOnly(EntryPoint = "get_ms2_scan_number_from_rt")] - public static int GetMs2ScanNumberFromRT(double rt, double precursorMz, double tolerancePpm) + public static int GetMs2ScanNumberFromRT(int handle, double rt, double precursorMz, double tolerancePpm) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -806,15 +917,13 @@ public static int GetMs2ScanNumberFromRT(double rt, double precursorMz, double t } return bestScan; } - catch - { - return -1; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetMs2ScanNumberFromRT (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_chromatogram")] - public static unsafe int GetChromatogram(int traceType, IntPtr filterPtr, double* massRangesStart, double* massRangesEnd, int massRangeCount, int startScan, int endScan, double* times, double* intensities, int maxLength) + public static unsafe int GetChromatogram(int handle, int traceType, IntPtr filterPtr, double* massRangesStart, double* massRangesEnd, int massRangeCount, int startScan, int endScan, double* times, double* intensities, int maxLength) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -854,8 +963,9 @@ public static unsafe int GetChromatogram(int traceType, IntPtr filterPtr, double } [UnmanagedCallersOnly(EntryPoint = "get_ms1_scan_number_from_rt")] - public static int GetMs1ScanNumberFromRT(double rt) + public static int GetMs1ScanNumberFromRT(int handle, double rt) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -877,15 +987,13 @@ public static int GetMs1ScanNumberFromRT(double rt) } return -1; } - catch - { - return -1; - } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetMs1ScanNumberFromRT (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_averaged_spectrum")] - public static unsafe int GetAveragedSpectrum(int* scanNumbers, int numScans, double* masses, double* intensities, int maxLength) + public static unsafe int GetAveragedSpectrum(int handle, int* scanNumbers, int numScans, double* masses, double* intensities, int maxLength) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -915,49 +1023,56 @@ public static unsafe int GetAveragedSpectrum(int* scanNumbers, int numScans, dou } [UnmanagedCallersOnly(EntryPoint = "get_instrument_count")] - public static int GetInstrumentCount() + public static int GetInstrumentCount(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; return _rawFile.InstrumentCount; } [UnmanagedCallersOnly(EntryPoint = "get_instrument_count_of_type")] - public static int GetInstrumentCountOfType(int type) + public static int GetInstrumentCountOfType(int handle, int type) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; return _rawFile.GetInstrumentCountOfType((Device)type); } [UnmanagedCallersOnly(EntryPoint = "is_open")] - public static int IsOpen() + public static int IsOpen(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; return _rawFile.IsOpen ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "is_error")] - public static int IsError() + public static int IsError(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 1; return _rawFile.IsError ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "in_acquisition")] - public static int InAcquisition() + public static int InAcquisition(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; return _rawFile.InAcquisition ? 1 : 0; } [UnmanagedCallersOnly(EntryPoint = "has_ms_data")] - public static int HasMsData() + public static int HasMsData(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; return _rawFile.HasMsData ? 1 : 0; } - private static unsafe int _getStatusLogValuesForRt(double rt, byte* buffer, int bufferSize) + private static unsafe int _getStatusLogValuesForRt(int handle, double rt, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -970,56 +1085,61 @@ private static unsafe int _getStatusLogValuesForRt(double rt, byte* buffer, int buffer[count] = 0; return bytes.Length; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in HasMsData (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_status_log_values_for_rt")] - public static unsafe int GetStatusLogValuesForRt(double rt, byte* buffer, int bufferSize) + public static unsafe int GetStatusLogValuesForRt(int handle, double rt, byte* buffer, int bufferSize) { - return _getStatusLogValuesForRt(rt, buffer, bufferSize); + var _rawFile = GetFile(handle); + return _getStatusLogValuesForRt(handle, rt, buffer, bufferSize); } [UnmanagedCallersOnly(EntryPoint = "get_status_log_values")] - public static unsafe int GetStatusLogValues(int scanNumber, byte* buffer, int bufferSize) + public static unsafe int GetStatusLogValues(int handle, int scanNumber, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var rt = _rawFile.RetentionTimeFromScanNumber(scanNumber); - return _getStatusLogValuesForRt(rt, buffer, bufferSize); + return _getStatusLogValuesForRt(handle, rt, buffer, bufferSize); } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in HasMsData (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_status_log_header")] - public static unsafe int GetStatusLogHeader(byte* buffer, int bufferSize) + public static unsafe int GetStatusLogHeader(int handle, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var info = _rawFile.GetStatusLogHeaderInformation(); if (info == null) return 0; - var res = string.Join("|", info.Select(x => x.Label + "###TYPE###" + (int)x.DataType)); + var res = string.Join("|", info.Select(x => x.Label + "###TYPE###" + (int)x.DataType + "###LEN###" + x.StringLengthOrPrecision)); var bytes = System.Text.Encoding.UTF8.GetBytes(res); int count = Math.Min(bytes.Length, bufferSize - 1); for (int i = 0; i < count; i++) buffer[i] = bytes[i]; buffer[count] = 0; return bytes.Length; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in HasMsData (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_status_log_count")] - public static int GetStatusLogCount() + public static int GetStatusLogCount(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { return _rawFile.GetStatusLogEntriesCount(); } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetStatusLogCount (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_trailer_extra_values")] - public static unsafe int GetTrailerExtraValues(int scanNumber, byte* buffer, int bufferSize) + public static unsafe int GetTrailerExtraValues(int handle, int scanNumber, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -1032,58 +1152,65 @@ public static unsafe int GetTrailerExtraValues(int scanNumber, byte* buffer, int buffer[count] = 0; return bytes.Length; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetStatusLogCount (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_trailer_extra_count")] - public static int GetTrailerExtraCount() + public static int GetTrailerExtraCount(int handle) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var header = _rawFile.GetTrailerExtraHeaderInformation(); return header != null ? header.Count() : 0; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetTrailerExtraCount (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_ms_order")] - public static int GetScanEventMsOrder(int scanNumber) + public static int GetScanEventMsOrder(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MSOrder; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MSOrder; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventMsOrder (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_mass_count")] - public static int GetScanEventMassCount(int scanNumber) + public static int GetScanEventMassCount(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return _rawFile.GetScanEventForScanNumber(scanNumber).MassCount; } catch { return -1; } + try { return _rawFile.GetScanEventForScanNumber(scanNumber).MassCount; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventMassCount (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_precursor_mass")] - public static double GetScanEventPrecursorMass(int scanNumber, int index) + public static double GetScanEventPrecursorMass(int handle, int scanNumber, int index) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return _rawFile.GetScanEventForScanNumber(scanNumber).GetMass(index); } catch { return -1; } + try { return _rawFile.GetScanEventForScanNumber(scanNumber).GetMass(index); } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventPrecursorMass (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_activation_type")] - public static int GetScanEventActivationType(int scanNumber, int index) + public static int GetScanEventActivationType(int handle, int scanNumber, int index) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).GetActivation(index); } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).GetActivation(index); } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventActivationType (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_collision_energy")] - public static double GetScanEventCollisionEnergy(int scanNumber, int index) + public static double GetScanEventCollisionEnergy(int handle, int scanNumber, int index) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return _rawFile.GetScanEventForScanNumber(scanNumber).GetEnergy(index); } catch { return -1; } + try { return _rawFile.GetScanEventForScanNumber(scanNumber).GetEnergy(index); } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventCollisionEnergy (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_stats")] - public static unsafe int GetScanStats(int scanNumber, double* data) + public static unsafe int GetScanStats(int handle, int scanNumber, double* data) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -1099,124 +1226,141 @@ public static unsafe int GetScanStats(int scanNumber, double* data) data[7] = stats.IsCentroidScan ? 1.0 : 0.0; return 8; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventCollisionEnergy (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_ultra")] - public static int GetScanFilterUltra(int scanNumber) + public static int GetScanFilterUltra(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Ultra; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Ultra; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterUltra (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_wideband")] - public static int GetScanFilterWideband(int scanNumber) + public static int GetScanFilterWideband(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Wideband; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Wideband; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterWideband (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_polarity")] - public static int GetScanFilterPolarity(int scanNumber) + public static int GetScanFilterPolarity(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Polarity; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Polarity; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterPolarity (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_ms_order")] - public static int GetScanFilterMsOrder(int scanNumber) + public static int GetScanFilterMsOrder(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MSOrder; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MSOrder; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterMsOrder (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_mass_analyzer")] - public static int GetScanFilterMassAnalyzer(int scanNumber) + public static int GetScanFilterMassAnalyzer(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MassAnalyzer; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).MassAnalyzer; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterMassAnalyzer (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_detector")] - public static int GetScanFilterDetector(int scanNumber) + public static int GetScanFilterDetector(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Detector; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Detector; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterDetector (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_scan_data")] - public static int GetScanFilterScanData(int scanNumber) + public static int GetScanFilterScanData(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).ScanData; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).ScanData; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterScanData (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_scan_mode")] - public static int GetScanFilterScanMode(int scanNumber) + public static int GetScanFilterScanMode(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).ScanMode; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).ScanMode; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterScanMode (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_accurate_mass")] - public static int GetScanFilterAccurateMass(int scanNumber) + public static int GetScanFilterAccurateMass(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).AccurateMass; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).AccurateMass; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterAccurateMass (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_ionization_mode")] - public static int GetScanFilterIonizationMode(int scanNumber) + public static int GetScanFilterIonizationMode(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).IonizationMode; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).IonizationMode; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterIonizationMode (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_lock")] - public static int GetScanFilterLock(int scanNumber) + public static int GetScanFilterLock(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Lock; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Lock; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterLock (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_turbo_scan")] - public static int GetScanFilterTurboScan(int scanNumber) + public static int GetScanFilterTurboScan(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).TurboScan; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).TurboScan; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterTurboScan (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_corona")] - public static int GetScanFilterCorona(int scanNumber) + public static int GetScanFilterCorona(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Corona; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Corona; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterCorona (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_dependent")] - public static int GetScanFilterDependent(int scanNumber) + public static int GetScanFilterDependent(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Dependent; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).Dependent; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterDependent (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_detector_value")] - public static double GetScanFilterDetectorValue(int scanNumber) + public static double GetScanFilterDetectorValue(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return _rawFile.GetScanEventForScanNumber(scanNumber).DetectorValue; } catch { return -1; } + try { return _rawFile.GetScanEventForScanNumber(scanNumber).DetectorValue; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterDetectorValue (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_compensation_voltage")] - public static int GetScanEventCompensationVoltage(int scanNumber) + public static int GetScanEventCompensationVoltage(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; - try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).CompensationVoltage; } catch { return -1; } + try { return (int)_rawFile.GetScanEventForScanNumber(scanNumber).CompensationVoltage; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventCompensationVoltage (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_event_compensation_voltage_value")] - public static double GetScanEventCompensationVoltageValue(int scanNumber) + public static double GetScanEventCompensationVoltageValue(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { @@ -1230,133 +1374,151 @@ public static double GetScanEventCompensationVoltageValue(int scanNumber) } return 0.0; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventCompensationVoltageValue (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "get_trailer_extra_header")] - public static unsafe int GetTrailerExtraHeader(byte* buffer, int bufferSize) + public static unsafe int GetTrailerExtraHeader(int handle, byte* buffer, int bufferSize) { + var _rawFile = GetFile(handle); if (_rawFile == null) return -1; try { var info = _rawFile.GetTrailerExtraHeaderInformation(); if (info == null) return 0; - var res = string.Join("|", info.Select(x => x.Label + "###TYPE###" + (int)x.DataType)); + var res = string.Join("|", info.Select(x => x.Label + "###TYPE###" + (int)x.DataType + "###LEN###" + x.StringLengthOrPrecision)); var bytes = System.Text.Encoding.UTF8.GetBytes(res); int count = Math.Min(bytes.Length, bufferSize - 1); for (int i = 0; i < count; i++) buffer[i] = bytes[i]; buffer[count] = 0; return bytes.Length; } - catch { return -1; } + catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanEventCompensationVoltageValue (fallback -1): " + ex.Message); return -1; } } [UnmanagedCallersOnly(EntryPoint = "close_raw_file")] - public static void CloseRawFile() + public static void CloseRawFile(int handle) { - _rawFile?.Dispose(); - _rawFile = null; + if (_files.TryRemove(handle, out var state)) + { + state.RawFile?.Dispose(); + } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_compensation_volt_type")] - public static int GetScanFilterCompensationVoltType(int scanNumber) + public static int GetScanFilterCompensationVoltType(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).CompensationVoltType; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).CompensationVoltType; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterCompensationVoltType (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_compensation_voltage_count")] - public static int GetScanFilterCompensationVoltageCount(int scanNumber) + public static int GetScanFilterCompensationVoltageCount(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return _rawFile.GetFilterForScanNumber(scanNumber).CompensationVoltageCount; } catch { return 0; } + try { return _rawFile.GetFilterForScanNumber(scanNumber).CompensationVoltageCount; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterCompensationVoltageCount (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_electron_capture_dissociation")] - public static int GetScanFilterElectronCaptureDissociation(int scanNumber) + public static int GetScanFilterElectronCaptureDissociation(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).ElectronCaptureDissociation; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).ElectronCaptureDissociation; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterElectronCaptureDissociation (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_electron_transfer_dissociation")] - public static int GetScanFilterElectronTransferDissociation(int scanNumber) + public static int GetScanFilterElectronTransferDissociation(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).ElectronTransferDissociation; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).ElectronTransferDissociation; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterElectronTransferDissociation (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_enhanced")] - public static int GetScanFilterEnhanced(int scanNumber) + public static int GetScanFilterEnhanced(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).Enhanced; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).Enhanced; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterEnhanced (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_source_fragmentation")] - public static int GetScanFilterSourceFragmentation(int scanNumber) + public static int GetScanFilterSourceFragmentation(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentation; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentation; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterSourceFragmentation (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_source_fragmentation_info_valid")] - public static int GetScanFilterSourceFragmentationInfoValid(int scanNumber) + public static int GetScanFilterSourceFragmentationInfoValid(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationInfoValid[0]; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationInfoValid[0]; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterSourceFragmentationInfoValid (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_source_fragmentation_type")] - public static int GetScanFilterSourceFragmentationType(int scanNumber) + public static int GetScanFilterSourceFragmentationType(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationType; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationType; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterSourceFragmentationType (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_source_fragmentation_value")] - public static double GetScanFilterSourceFragmentationValue(int scanNumber) + public static double GetScanFilterSourceFragmentationValue(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0.0; - try { return _rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationValue(0); } catch { return 0.0; } + try { return _rawFile.GetFilterForScanNumber(scanNumber).SourceFragmentationValue(0); } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterSourceFragmentationValue (fallback 0.0): " + ex.Message); return 0.0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_supplemental_activation")] - public static int GetScanFilterSupplementalActivation(int scanNumber) + public static int GetScanFilterSupplementalActivation(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SupplementalActivation; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).SupplementalActivation; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterSupplementalActivation (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_mass_precision")] - public static int GetScanFilterMassPrecision(int scanNumber) + public static int GetScanFilterMassPrecision(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).MassPrecision; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).MassPrecision; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterMassPrecision (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_multi_notch")] - public static int GetScanFilterMultiNotch(int scanNumber) + public static int GetScanFilterMultiNotch(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).MultiNotch; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).MultiNotch; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterMultiNotch (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_multiplex")] - public static int GetScanFilterMultiplex(int scanNumber) + public static int GetScanFilterMultiplex(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).Multiplex; } catch { return 0; } + try { return (int)_rawFile.GetFilterForScanNumber(scanNumber).Multiplex; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterMultiplex (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_unique_mass_count")] - public static int GetScanFilterUniqueMassCount(int scanNumber) + public static int GetScanFilterUniqueMassCount(int handle, int scanNumber) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; - try { return _rawFile.GetFilterForScanNumber(scanNumber).UniqueMassCount; } catch { return 0; } + try { return _rawFile.GetFilterForScanNumber(scanNumber).UniqueMassCount; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterUniqueMassCount (fallback 0): " + ex.Message); return 0; } } - private static double GetFilterDouble(int scanNumber, string name) + private static double GetFilterDouble(int handle, int scanNumber, string name) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0.0; try { var filter = _rawFile.GetFilterForScanNumber(scanNumber); @@ -1365,11 +1527,12 @@ private static double GetFilterDouble(int scanNumber, string name) var val = prop.GetValue(filter); if (val == null) return 0.0; return (double)Convert.ChangeType(val, typeof(double)); - } catch { return 0.0; } + } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterUniqueMassCount (fallback 0.0): " + ex.Message); return 0.0; } } - private static int GetFilterInt(int scanNumber, string name) + private static int GetFilterInt(int handle, int scanNumber, string name) { + var _rawFile = GetFile(handle); if (_rawFile == null) return 0; try { var filter = _rawFile.GetFilterForScanNumber(scanNumber); @@ -1378,99 +1541,215 @@ private static int GetFilterInt(int scanNumber, string name) var val = prop.GetValue(filter); if (val == null) return 0; return (int)Convert.ChangeType(val, typeof(int)); - } catch { return 0; } + } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetScanFilterUniqueMassCount (fallback 0): " + ex.Message); return 0; } } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_higher_energy_cid")] - public static int GetScanFilterHigherEnergyCID(int scanNumber) + public static int GetScanFilterHigherEnergyCID(int handle, int scanNumber) { - return GetFilterInt(scanNumber, "HigherEnergyCID") != 0 ? GetFilterInt(scanNumber, "HigherEnergyCID") : GetFilterInt(scanNumber, "HigherEnergyCid"); + var _rawFile = GetFile(handle); + return GetFilterInt(handle, scanNumber, "HigherEnergyCID") != 0 ? GetFilterInt(handle, scanNumber, "HigherEnergyCID") : GetFilterInt(handle, scanNumber, "HigherEnergyCid"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_higher_energy_cid_value")] - public static double GetScanFilterHigherEnergyCIDValue(int scanNumber) + public static double GetScanFilterHigherEnergyCIDValue(int handle, int scanNumber) { - double val = GetFilterDouble(scanNumber, "HigherEnergyCIDValue"); - if (val == 0.0) val = GetFilterDouble(scanNumber, "HigherEnergyCidValue"); + var _rawFile = GetFile(handle); + double val = GetFilterDouble(handle, scanNumber, "HigherEnergyCIDValue"); + if (val == 0.0) val = GetFilterDouble(handle, scanNumber, "HigherEnergyCidValue"); return val; } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_electron_capture_dissociation_value")] - public static double GetScanFilterElectronCaptureDissociationValue(int scanNumber) + public static double GetScanFilterElectronCaptureDissociationValue(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ElectronCaptureDissociationValue"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ElectronCaptureDissociationValue"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_electron_transfer_dissociation_value")] - public static double GetScanFilterElectronTransferDissociationValue(int scanNumber) + public static double GetScanFilterElectronTransferDissociationValue(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ElectronTransferDissociationValue"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ElectronTransferDissociationValue"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_multiple_photon_dissociation")] - public static int GetScanFilterMultiplePhotonDissociation(int scanNumber) + public static int GetScanFilterMultiplePhotonDissociation(int handle, int scanNumber) { - return GetFilterInt(scanNumber, "MultiplePhotonDissociation"); + var _rawFile = GetFile(handle); + return GetFilterInt(handle, scanNumber, "MultiplePhotonDissociation"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_multiple_photon_dissociation_value")] - public static double GetScanFilterMultiplePhotonDissociationValue(int scanNumber) + public static double GetScanFilterMultiplePhotonDissociationValue(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "MultiplePhotonDissociationValue"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "MultiplePhotonDissociationValue"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_pulsed_q_dissociation")] - public static int GetScanFilterPulsedQDissociation(int scanNumber) + public static int GetScanFilterPulsedQDissociation(int handle, int scanNumber) { - return GetFilterInt(scanNumber, "PulsedQDissociation"); + var _rawFile = GetFile(handle); + return GetFilterInt(handle, scanNumber, "PulsedQDissociation"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_pulsed_q_dissociation_value")] - public static double GetScanFilterPulsedQDissociationValue(int scanNumber) + public static double GetScanFilterPulsedQDissociationValue(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "PulsedQDissociationValue"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "PulsedQDissociationValue"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_param_a")] - public static double GetScanFilterParamA(int scanNumber) + public static double GetScanFilterParamA(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ParamA"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ParamA"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_param_b")] - public static double GetScanFilterParamB(int scanNumber) + public static double GetScanFilterParamB(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ParamB"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ParamB"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_param_f")] - public static double GetScanFilterParamF(int scanNumber) + public static double GetScanFilterParamF(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ParamF"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ParamF"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_param_r")] - public static double GetScanFilterParamR(int scanNumber) + public static double GetScanFilterParamR(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ParamR"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ParamR"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_param_v")] - public static double GetScanFilterParamV(int scanNumber) + public static double GetScanFilterParamV(int handle, int scanNumber) { - return GetFilterDouble(scanNumber, "ParamV"); + var _rawFile = GetFile(handle); + return GetFilterDouble(handle, scanNumber, "ParamV"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_field_free_region")] - public static int GetScanFilterFieldFreeRegion(int scanNumber) + public static int GetScanFilterFieldFreeRegion(int handle, int scanNumber) { - return GetFilterInt(scanNumber, "FieldFreeRegion"); + var _rawFile = GetFile(handle); + return GetFilterInt(handle, scanNumber, "FieldFreeRegion"); } [UnmanagedCallersOnly(EntryPoint = "get_scan_filter_index_to_multiple_activation_index")] - public static int GetScanFilterIndexToMultipleActivationIndex(int scanNumber) + public static int GetScanFilterIndexToMultipleActivationIndex(int handle, int scanNumber) + { + var _rawFile = GetFile(handle); + return GetFilterInt(handle, scanNumber, "IndexToMultipleActivationIndex"); + } + [UnmanagedCallersOnly(EntryPoint = "select_instrument")] + public static void SelectInstrument(int handle, int deviceType, int deviceNumber) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return; + try { + _rawFile.SelectInstrument((Device)deviceType, deviceNumber); + } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in SelectInstrument (ignored): " + ex.Message); } + } + + [UnmanagedCallersOnly(EntryPoint = "get_instrument_method_count")] + public static int GetInstrumentMethodCount(int handle) + { + var state = GetState(handle); + return state != null ? state.CachedMethodCount : 0; + } + + [UnmanagedCallersOnly(EntryPoint = "get_instrument_method")] + public static unsafe int GetInstrumentMethod(int handle, int index, byte* buffer, int maxLength) + { + var _rawFile = GetFile(handle); + 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 (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetInstrumentMethodCount (fallback -1): " + ex.Message); return -1; } + } + [UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_index")] + public static int GetAutoSamplerTrayIndex(int handle) + { + var _rawFile = GetFile(handle); + 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(int handle) { - return GetFilterInt(scanNumber, "IndexToMultipleActivationIndex"); + var _rawFile = GetFile(handle); + 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(int handle, byte* buffer, int maxLength) + { + var _rawFile = GetFile(handle); + 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 (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetAutoSamplerVialIndex (fallback 0): " + ex.Message); return 0; } + } + + [UnmanagedCallersOnly(EntryPoint = "get_autosampler_tray_shape")] + public static int GetAutoSamplerTrayShape(int handle) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return 0; + try { return (int)_rawFile.AutoSamplerInformation.TrayShape; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetAutoSamplerTrayShape (fallback 0): " + ex.Message); return 0; } + } + + [UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray")] + public static int GetAutoSamplerVialsPerTray(int handle) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return -1; + try { return _rawFile.AutoSamplerInformation.VialsPerTray; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetAutoSamplerVialsPerTray (fallback -1): " + ex.Message); return -1; } + } + + [UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_x")] + public static int GetAutoSamplerVialsPerTrayX(int handle) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return -1; + try { return _rawFile.AutoSamplerInformation.VialsPerTrayX; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetAutoSamplerVialsPerTrayX (fallback -1): " + ex.Message); return -1; } + } + + [UnmanagedCallersOnly(EntryPoint = "get_autosampler_vials_per_tray_y")] + public static int GetAutoSamplerVialsPerTrayY(int handle) + { + var _rawFile = GetFile(handle); + if (_rawFile == null) return -1; + try { return _rawFile.AutoSamplerInformation.VialsPerTrayY; } catch (Exception ex) { Console.Error.WriteLine("[native-fisher-py] Exception in GetAutoSamplerVialsPerTrayY (fallback -1): " + ex.Message); return -1; } } } } diff --git a/native_fisher_py/.gitignore b/native_fisher_py/.gitignore index c8f0442..fcb12b7 100644 --- a/native_fisher_py/.gitignore +++ b/native_fisher_py/.gitignore @@ -70,3 +70,4 @@ docs/_build/ # Pyenv .python-version +README.md diff --git a/native_fisher_py/pyproject.toml b/native_fisher_py/pyproject.toml index 9790d6f..7a98f08 100644 --- a/native_fisher_py/pyproject.toml +++ b/native_fisher_py/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "maturin" name = "native-fisher-py" description = "Drop-in replacement for fisher-py without dotnet required" requires-python = ">=3.8" -readme = "../README.md" +readme = "README.md" classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", @@ -25,4 +25,4 @@ tests = [ [tool.maturin] python-source = "python" module-name = "native_fisher_py.native_fisher_py_backend" -include = ["python/native_fisher_py/ThermoNativeReader.*"] +include = ["python/native_fisher_py/ThermoNativeReader.*", "python/native_fisher_py/*.dll"] diff --git a/native_fisher_py/python/native_fisher_py/OpenMcdf.Extensions.dll b/native_fisher_py/python/native_fisher_py/OpenMcdf.Extensions.dll new file mode 100644 index 0000000..f60faf0 Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/OpenMcdf.Extensions.dll differ diff --git a/native_fisher_py/python/native_fisher_py/OpenMcdf.dll b/native_fisher_py/python/native_fisher_py/OpenMcdf.dll new file mode 100644 index 0000000..56ee66d Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/OpenMcdf.dll differ diff --git a/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.BackgroundSubtraction.dll b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.BackgroundSubtraction.dll new file mode 100644 index 0000000..17ae462 Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.BackgroundSubtraction.dll differ diff --git a/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.Data.dll b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.Data.dll new file mode 100644 index 0000000..a77647e Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.Data.dll differ diff --git a/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.MassPrecisionEstimator.dll b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.MassPrecisionEstimator.dll new file mode 100644 index 0000000..2e134b6 Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.MassPrecisionEstimator.dll differ diff --git a/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.RawFileReader.dll b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.RawFileReader.dll new file mode 100644 index 0000000..7330949 Binary files /dev/null and b/native_fisher_py/python/native_fisher_py/ThermoFisher.CommonCore.RawFileReader.dll differ diff --git a/native_fisher_py/python/native_fisher_py/data/classes.py b/native_fisher_py/python/native_fisher_py/data/classes.py index 276526d..f880994 100644 --- a/native_fisher_py/python/native_fisher_py/data/classes.py +++ b/native_fisher_py/python/native_fisher_py/data/classes.py @@ -147,69 +147,70 @@ def get_hash_code(self): return 0 def perform_default_settings(self): pass class ScanFilter(CommonCoreDataObject): - def __init__(self, scan_number=0): + def __init__(self, handle=0, scan_number=0): + self._handle = handle self._scan_number = scan_number def __str__(self): from . import get_scan_filter_string return get_scan_filter_string(self._scan_number) @property def name(self): - return get_scan_event_string(self._scan_number) + return get_scan_event_string(self._handle, self._scan_number) @property def ms_order(self): - return MsOrderType(get_ms_order(self._scan_number)) + return MsOrderType(get_ms_order(self._handle, self._scan_number)) @property def mass_analyzer(self): - return MassAnalyzerType(get_mass_analyzer(self._scan_number)) + return MassAnalyzerType(get_mass_analyzer(self._handle, self._scan_number)) @property def polarity(self): - return PolarityType(get_scan_filter_polarity(self._scan_number)) + return PolarityType(get_scan_filter_polarity(self._handle, self._scan_number)) @property def scan_data(self): - return ScanDataType(get_scan_filter_scan_data(self._scan_number)) + return ScanDataType(get_scan_filter_scan_data(self._handle, self._scan_number)) @property def ultra(self): - return TriState(get_scan_filter_ultra(self._scan_number)) + return TriState(get_scan_filter_ultra(self._handle, self._scan_number)) @property def wideband(self): - return TriState(get_scan_filter_wideband(self._scan_number)) + return TriState(get_scan_filter_wideband(self._handle, self._scan_number)) @property def detector(self): - return DetectorType(get_scan_filter_detector(self._scan_number)) + return DetectorType(get_scan_filter_detector(self._handle, self._scan_number)) @property def compensation_voltage(self): - return TriState(get_scan_event_compensation_voltage(self._scan_number)) + return TriState(get_scan_event_compensation_voltage(self._handle, self._scan_number)) @property def compensation_voltage_value(self): - return get_scan_event_compensation_voltage_value(self._scan_number) + return get_scan_event_compensation_voltage_value(self._handle, self._scan_number) @property def scan_mode(self): - return ScanModeType(get_scan_filter_scan_mode(self._scan_number)) + return ScanModeType(get_scan_filter_scan_mode(self._handle, self._scan_number)) @property def accurate_mass(self): - return EventAccurateMass(get_scan_filter_accurate_mass(self._scan_number)) + return EventAccurateMass(get_scan_filter_accurate_mass(self._handle, self._scan_number)) @property def ionization_mode(self): - return IonizationModeType(get_scan_filter_ionization_mode(self._scan_number)) + return IonizationModeType(get_scan_filter_ionization_mode(self._handle, self._scan_number)) @property def lock(self): - return TriState(get_scan_filter_lock(self._scan_number)) + return TriState(get_scan_filter_lock(self._handle, self._scan_number)) @property def meta_filters(self): # This will be implemented in the native layer to return a list of filter strings - return get_scan_filter_meta_filters(self._scan_number) + return get_scan_filter_meta_filters(self._handle, self._scan_number) @property def turbo_scan(self): - return TriState(get_scan_filter_turbo_scan(self._scan_number)) + return TriState(get_scan_filter_turbo_scan(self._handle, self._scan_number)) @property def corona(self): - return TriState(get_scan_filter_corona(self._scan_number)) + return TriState(get_scan_filter_corona(self._handle, self._scan_number)) @property def dependent(self): - return TriState(get_scan_filter_dependent(self._scan_number)) + return TriState(get_scan_filter_dependent(self._handle, self._scan_number)) @property def detector_value(self): - return get_scan_filter_detector_value(self._scan_number) + return get_scan_filter_detector_value(self._handle, self._scan_number) @property def source_fragmentation(self): @@ -279,12 +280,12 @@ def enhanced(self): return TriState(get_scan_filter_enhanced(self._scan_number)) @property def field_free_region(self): - return FieldFreeRegionType(get_scan_filter_field_free_region(self._scan_number)) + return FieldFreeRegionType(get_scan_filter_field_free_region(self._handle, self._scan_number)) @property def get_source_fragmentation_info_valid(self): return True @property def index_to_multiple_activation_index(self): - return get_scan_filter_index_to_multiple_activation_index(self._scan_number) + return get_scan_filter_index_to_multiple_activation_index(self._handle, self._scan_number) @property def locale_name(self): return "en-US" @property @@ -411,6 +412,7 @@ class MsOrderType(EnumBase): setattr(MsOrderType, name, MsOrderType(["Any", "Ms1", "Ms2", "Ms3", "Ms4", "Ms5", "Ms6", "Ms7", "Ms8", "Ms9", "Ms10", "Ng", "Nl", "Par"].index(name))) getattr(MsOrderType, name).name = name MSOrder = MsOrderType +MsOrderType.Ms = MsOrderType.Ms1 class MassAnalyzer(EnumBase): Any = 0; ITMS = 1; TQMS = 2; SQMS = 3; TOFMS = 4; FTMS = 5; Sector = 6; MassAnalyzerFTMS = 5; MassAnalyzerITMS = 1; MassAnalyzerSQMS = 3; MassAnalyzerSector = 6; MassAnalyzerTOFMS = 4; MassAnalyzerTQMS = 2 @@ -721,10 +723,19 @@ def get(self, key, default=None): class HeaderItem(CommonCoreDataObject): def __init__(self, data): + self._string_length_or_precision = 0 if "###TYPE###" in data: parts = data.split("###TYPE###") self._label = parts[0] - try: self._data_type = GenericDataTypes(int(parts[1])) + rest = parts[1] + if "###LEN###" in rest: + subparts = rest.split("###LEN###") + type_val = subparts[0] + try: self._string_length_or_precision = int(subparts[1]) + except: pass + else: + type_val = rest + try: self._data_type = GenericDataTypes(int(type_val)) except: self._data_type = GenericDataTypes.NULL else: self._label = data @@ -733,6 +744,8 @@ def __init__(self, data): @property def label(self): return self._label @property + def string_length_or_precision(self): return self._string_length_or_precision + @property def data_type(self): return self._data_type @property def is_numeric(self): @@ -752,7 +765,8 @@ def format_value(self): raise NotImplementedError class StatusLogValues(CommonCoreDataObject): - def __init__(self, retention_time=0.0, values=None): + def __init__(self, handle=0, retention_time=0.0, values=None): + self._handle = handle self._retention_time = retention_time self._values = values or [] @property @@ -761,7 +775,8 @@ def retention_time(self): return self._retention_time def values(self): return self._values class TuneDataValues(CommonCoreDataObject): - def __init__(self, id=0, values=None): + def __init__(self, handle=0, id=0, values=None): + self._handle = handle self._id = id self._values = values or [] @property @@ -770,18 +785,19 @@ def id(self): return self._id def values(self): return self._values class Reaction(CommonCoreDataObject): - def __init__(self, scan_number=0, index=0): + def __init__(self, handle=0, scan_number=0, index=0): + self._handle = handle self._scan_number = scan_number self._index = index @property def precursor_mass(self): - return get_scan_event_precursor_mass(self._scan_number, self._index) + return get_scan_event_precursor_mass(self._handle, self._scan_number, self._index) @property def activation_type(self): - return ActivationType(get_scan_event_activation_type(self._scan_number, self._index)) + return ActivationType(get_scan_event_activation_type(self._handle, self._scan_number, self._index)) @property def collision_energy(self): - return get_scan_event_collision_energy(self._scan_number, self._index) + return get_scan_event_collision_energy(self._handle, self._scan_number, self._index) @property def collision_energy_valid(self): raise NotImplementedError @property @@ -882,9 +898,27 @@ def to_centroid(self): return None def tolerance_unit(self): return 0 class CentroidStream(CommonCoreDataObject): - def __init__(self, masses=None, intensities=None): + def __init__(self, masses=None, intensities=None, baselines=None, noises=None, charges=None, base_peak_noise=0.0, base_peak_resolution=0.0, scan_number=0): self._masses = masses if masses is not None else np.array([]) self._intensities = intensities if intensities is not None else np.array([]) + self._baselines = baselines if baselines is not None else np.array([]) + self._noises = noises if noises is not None else np.array([]) + self._charges = charges if charges is not None else np.array([]) + self._base_peak_noise = base_peak_noise + self._base_peak_resolution = base_peak_resolution + self._scan_number = scan_number + + def clear(self): + self._masses = np.array([]) + self._intensities = np.array([]) + self._baselines = np.array([]) + self._noises = np.array([]) + self._charges = np.array([]) + + def clone(self): return self + def deep_clone(self): + import copy + return copy.deepcopy(self) @property def base_intensity(self): return np.max(self._intensities) if self._intensities.size > 0 else 0.0 @@ -893,57 +927,40 @@ def base_peak_intensity(self): return self.base_intensity @property def base_peak_mass(self): return self._masses[np.argmax(self._intensities)] if self._intensities.size > 0 else 0.0 @property - def base_peak_noise(self): raise NotImplementedError + def base_peak_noise(self): return self._base_peak_noise @property - def base_peak_resolution(self): raise NotImplementedError + def base_peak_resolution(self): return self._base_peak_resolution @property - def baselines(self): raise NotImplementedError + def baselines(self): return self._baselines @property - def charges(self): raise NotImplementedError - def clear(self): raise NotImplementedError - def clone(self): return self + def charges(self): return self._charges @property - def coefficients(self): - if _IS_SPHINX: return np.array([]) - raise NotImplementedError + def coefficients_count(self): return 0 @property - def coefficients_count(self): - if _IS_SPHINX: return 0 - raise NotImplementedError - def deep_clone(self): - if _IS_SPHINX: return self - raise NotImplementedError + def coefficients(self): return np.array([]) @property - def flags(self): - if _IS_SPHINX: return [] - raise NotImplementedError - def get_centroids(self): - if _IS_SPHINX: return [] - raise NotImplementedError - def get_label_peak(self, i): - if _IS_SPHINX: return None - raise NotImplementedError - def get_label_peaks(self): - if _IS_SPHINX: return [] - raise NotImplementedError + def flags(self): return 0 + def get_centroids(self): return self._masses, self._intensities + def get_label_peak(self, index): return None + def get_label_peaks(self): return None + @property + def noises(self): return self._noises @property def intensities(self): return self._intensities @property def length(self): return len(self._masses) if self._masses is not None else 0 @property def masses(self): return self._masses - @property - def noises(self): raise NotImplementedError def refresh_base_details(self): pass @property def resolutions(self): return np.array([]) @property - def scan_number(self): return 0 - def set_label_peaks(self, p): pass + def scan_number(self): return self._scan_number + def set_label_peaks(self, peaks): pass @property - def sum_intensities(self): return 0.0 + def sum_intensities(self): return np.sum(self._intensities) if self._intensities.size > 0 else 0.0 @property - def sum_masses(self): return 0.0 + def sum_masses(self): return np.sum(self._masses) if self._masses.size > 0 else 0.0 def to_scan(self): return None def to_segmented_scan(self): return None def to_simple_scan(self): return None @@ -1009,37 +1026,41 @@ def valid(self): return 1 class InstrumentData(CommonCoreDataObject): + def __init__(self, handle=0): + self._handle = handle @property - def axis_label_x(self): return get_instrument_axis_label_x() + def axis_label_x(self): return get_instrument_axis_label_x(self._handle) @property - def axis_label_y(self): return get_instrument_axis_label_y() + def axis_label_y(self): return get_instrument_axis_label_y(self._handle) @property def channel_labels(self): return [] def clone(self): return self @property - def flags(self): return get_instrument_flags() + def flags(self): return get_instrument_flags(self._handle) @property - def has_accurate_mass_precursors(self): return get_instrument_has_accurate_mass_precursors() + def has_accurate_mass_precursors(self): return get_instrument_has_accurate_mass_precursors(self._handle) @property - def is_tsq_quantum_file(self): return get_instrument_is_tsq_quantum_file() + def is_tsq_quantum_file(self): return get_instrument_is_tsq_quantum_file(self._handle) @property - def is_valid(self): return get_instrument_is_valid() + def is_valid(self): return get_instrument_is_valid(self._handle) @property - def units(self): return DataUnits(get_instrument_units()) + def units(self): return DataUnits(get_instrument_units(self._handle)) @property - def name(self) -> str: return get_instrument_name() + def name(self) -> str: return get_instrument_name(self._handle) @property - def model(self) -> str: return get_instrument_model() + def model(self) -> str: return get_instrument_model(self._handle) @property - def serial_number(self) -> str: return get_instrument_serial_number() + def serial_number(self) -> str: return get_instrument_serial_number(self._handle) @property - def software_version(self) -> str: return get_instrument_software_version() + def software_version(self) -> str: return get_instrument_software_version(self._handle) @property - def hardware_version(self) -> str: return get_instrument_hardware_version() + def hardware_version(self) -> str: return get_instrument_hardware_version(self._handle) class SampleInformation(CommonCoreDataObject): + def __init__(self, handle=0): + self._handle = handle @property - def barcode(self): return get_sample_barcode() + def barcode(self): return get_sample_barcode(self._handle) @property def barcode_status(self): if _IS_SPHINX: return 0 @@ -1053,18 +1074,16 @@ def calibration_level(self): if _IS_SPHINX: return 0 raise NotImplementedError @property - def comment(self): return get_sample_comment() + def comment(self): return get_sample_comment(self._handle) def deep_copy(self): raise NotImplementedError @property - def dilution_factor(self): return get_sample_dilution_factor() + def dilution_factor(self): return get_sample_dilution_factor(self._handle) @property def injection_volume(self): - if _IS_SPHINX: return 0.0 - raise NotImplementedError + return get_sample_injection_volume(self._handle) @property def instrument_method_file(self): - if _IS_SPHINX: return "" - raise NotImplementedError + return get_sample_instrument_method_file(self._handle) @property def istd_amount(self): if _IS_SPHINX: return 0.0 @@ -1078,15 +1097,15 @@ def processing_method_file(self): if _IS_SPHINX: return "" raise NotImplementedError @property - def row_number(self): return get_sample_row_number() + def row_number(self): return get_sample_row_number(self._handle) @property - def sample_id(self): return get_sample_id() + def sample_id(self): return get_sample_id(self._handle) @property - def sample_name(self): return get_sample_name() + def sample_name(self): return get_sample_name(self._handle) @property - def vial(self): return get_sample_vial() + def vial(self): return get_sample_vial(self._handle) @property - def sample_type(self): return SampleType(get_sample_type()) + def sample_type(self): return SampleType(get_sample_type(self._handle)) @property def sample_volume(self): return 0.0 @property @@ -1096,21 +1115,25 @@ def user_text(self): return [] @property def vial(self): return "" @property - def raw_file_name(self) -> str: return get_file_name() + def raw_file_name(self) -> str: return get_file_name(self._handle) + @property + def path(self) -> str: return get_path(self._handle) @property - def path(self) -> str: return get_path() + def autosampler_information(self): return AutoSamplerInformation(self._handle) class FileHeader(CommonCoreDataObject): + def __init__(self, handle=0): + self._handle = handle @property - def creation_date(self) -> str: return get_creation_date() + def creation_date(self) -> str: return get_creation_date(self._handle) @property - def who_created_id(self) -> str: return get_creator_id() + def who_created_id(self) -> str: return get_creator_id(self._handle) @property - def file_description(self): return get_file_description() + def file_description(self): return get_file_description(self._handle) @property def file_type(self): return FileType.RawFile @property - def modified_date(self): return get_modified_date() + def modified_date(self): return get_modified_date(self._handle) @property def number_of_times_calibrated(self): if _IS_SPHINX: return -1 @@ -1124,11 +1147,11 @@ def revision(self): if _IS_SPHINX: return -1 raise NotImplementedError @property - def who_created_logon(self): return get_who_created_logon() + def who_created_logon(self): return get_who_created_logon(self._handle) @property - def who_modified_id(self): return get_who_modified_id() + def who_modified_id(self): return get_who_modified_id(self._handle) @property - def who_modified_logon(self): return get_who_modified_logon() + def who_modified_logon(self): return get_who_modified_logon(self._handle) class FileError(CommonCoreDataObject): @property @@ -1143,43 +1166,40 @@ def has_warning(self): return 0 def warning_message(self): return "" class AutoSamplerInformation(CommonCoreDataObject): + def __init__(self, handle=0): + self._handle = handle @property def tray_index(self): - if _IS_SPHINX: return -1 - raise NotImplementedError + return get_autosampler_tray_index(self._handle) @property def tray_name(self): - if _IS_SPHINX: return "Any" - raise NotImplementedError + return get_autosampler_tray_name(self._handle) @property def tray_shape(self): - if _IS_SPHINX: return TrayShape.Unknown - raise NotImplementedError + return TrayShape(get_autosampler_tray_shape(self._handle)) @property def tray_shape_as_string(self): - if _IS_SPHINX: return "Unknown" - raise NotImplementedError + return str(self.tray_shape) @property def vial_index(self): - if _IS_SPHINX: return -1 - raise NotImplementedError + return get_autosampler_vial_index(self._handle) @property def vials_per_tray(self): - if _IS_SPHINX: return -1 - raise NotImplementedError + return get_autosampler_vials_per_tray(self._handle) @property def vials_per_tray_x(self): - if _IS_SPHINX: return -1 - raise NotImplementedError + return get_autosampler_vials_per_tray_x(self._handle) @property def vials_per_tray_y(self): - if _IS_SPHINX: return -1 - raise NotImplementedError + return get_autosampler_vials_per_tray_y(self._handle) class RunHeader(CommonCoreDataObject): - def __init__(self, raw_file=None): self._raw_file = raw_file + def __init__(self, handle=0, raw_file=None): + self._handle = handle + self._raw_file = raw_file + self._handle = raw_file._handle if raw_file else 0 @property - def start_time(self) -> float: return get_start_time() + def start_time(self) -> float: return get_start_time(self._handle) @property def first_spectrum(self) -> int: return self._raw_file.first_scan if self._raw_file else 1 @property @@ -1238,7 +1258,9 @@ def tolerance_unit(self): raise NotImplementedError class RunHeaderEx(CommonCoreDataObject): - def __init__(self, raw_file): self._raw_file = raw_file + def __init__(self, raw_file): + self._raw_file = raw_file + self._handle = raw_file._handle if raw_file else 0 @property def spectra_count(self): return self._raw_file.number_of_scans @property @@ -1246,23 +1268,23 @@ def first_spectrum(self): return self._raw_file.first_scan @property def last_spectrum(self): return self._raw_file.last_scan @property - def start_time(self): return get_start_time() + def start_time(self): return get_start_time(self._handle) @property - def end_time(self): return get_end_time() + def end_time(self): return get_end_time(self._handle) @property - def mass_resolution(self): return get_mass_resolution() + def mass_resolution(self): return get_mass_resolution(self._handle) @property - def expected_runtime(self): return get_expected_runtime() + def expected_runtime(self): return get_expected_runtime(self._handle) @property - def max_integrated_intensity(self): return get_max_integrated_intensity() + def max_integrated_intensity(self): return get_max_integrated_intensity(self._handle) @property - def max_intensity(self): return get_max_intensity() + def max_intensity(self): return get_max_intensity(self._handle) @property - def trailer_extra_count(self): return get_trailer_extra_count() + def trailer_extra_count(self): return get_trailer_extra_count(self._handle) @property - def low_mass(self): return get_low_mass() + def low_mass(self): return get_low_mass(self._handle) @property - def high_mass(self): return get_high_mass() + def high_mass(self): return get_high_mass(self._handle) @property def error_message(self): if _IS_SPHINX: return "" @@ -1281,6 +1303,8 @@ def warning_message(self): raise NotImplementedError class WrappedRunHeader(CommonCoreDataObject): + def __init__(self, handle=0): + self._handle = handle @property def comment_1(self): if _IS_SPHINX: return "" @@ -1307,44 +1331,44 @@ def filter_mass_precision(self): raise NotImplementedError @property def high_mass(self): - return get_high_mass() + return get_high_mass(self._handle) @property def in_acquisition(self): return in_acquisition() @property def low_mass(self): - return get_low_mass() + return get_low_mass(self._handle) @property def mass_resolution(self): - return get_mass_resolution() + return get_mass_resolution(self._handle) @property def max_integrated_intensity(self): - return get_max_integrated_intensity() + return get_max_integrated_intensity(self._handle) @property def max_intensity(self): - return get_max_intensity() + return get_max_intensity(self._handle) @property def spectra_count(self): - return get_num_scans() + return get_num_scans(self._handle) @property def status_log_count(self): - return get_status_log_count() + return get_status_log_count(self._handle) @property def trailer_extra_count(self): - return get_trailer_extra_count() + return get_trailer_extra_count(self._handle) @property def trailer_scan_event_count(self): if _IS_SPHINX: return 0 raise NotImplementedError @property def tune_data_count(self): - return get_tune_data_count() + return get_tune_data_count(self._handle) @property def first_spectrum(self): - return get_first_scan() + return get_first_scan(self._handle) @property def last_spectrum(self): - return get_last_scan() + return get_last_scan(self._handle) @property def start_time(self): if _IS_SPHINX: return 0.0 @@ -1355,61 +1379,62 @@ def tolerance_unit(self): raise NotImplementedError class ScanEvent(CommonCoreDataObject): - def __init__(self, scan_number=0): + def __init__(self, handle=0, scan_number=0): + self._handle = handle self._scan_number = scan_number @property def ms_order(self): - return MsOrderType(get_scan_event_ms_order(self._scan_number)) + return MsOrderType(get_scan_event_ms_order(self._handle, self._scan_number)) @property def mass_count(self): - return get_scan_event_mass_count(self._scan_number) + return get_scan_event_mass_count(self._handle, self._scan_number) def get_mass(self, index): - return get_scan_event_precursor_mass(self._scan_number, index) + return get_scan_event_precursor_mass(self._handle, self._scan_number, index) def get_activation(self, index): - return ActivationType(get_scan_event_activation_type(self._scan_number, index)) + return ActivationType(get_scan_event_activation_type(self._handle, self._scan_number, index)) def get_energy(self, index): - return get_scan_event_collision_energy(self._scan_number, index) + return get_scan_event_collision_energy(self._handle, self._scan_number, index) def get_reaction(self, index): - return Reaction(self._scan_number, index) + return Reaction(self._handle, self._scan_number, index) @property def name(self): - return get_scan_event_string(self._scan_number) + return get_scan_event_string(self._handle, self._scan_number) @property def accurate_mass(self): - return EventAccurateMass(get_scan_filter_accurate_mass(self._scan_number)) + return EventAccurateMass(get_scan_filter_accurate_mass(self._handle, self._scan_number)) @property def mass_analyzer(self) -> int: return MassAnalyzer(get_scan_filter_mass_analyzer(self._scan_number)) @property def polarity(self) -> int: - return PolarityType(get_scan_filter_polarity(self._scan_number)) + return PolarityType(get_scan_filter_polarity(self._handle, self._scan_number)) @property def scan_mode(self) -> int: - return ScanModeType(get_scan_filter_scan_mode(self._scan_number)) + return ScanModeType(get_scan_filter_scan_mode(self._handle, self._scan_number)) @property def ionization_mode(self) -> int: - return IonizationModeType(get_scan_filter_ionization_mode(self._scan_number)) + return IonizationModeType(get_scan_filter_ionization_mode(self._handle, self._scan_number)) @property def is_valid(self) -> bool: - return bool(get_instrument_is_valid()) + return bool(get_instrument_is_valid(self._handle)) @property def compensation_volt_type(self): return CompensationVoltageType(get_scan_filter_compensation_volt_type(self._scan_number)) @property def compensation_voltage(self): - return TriState(get_scan_event_compensation_voltage(self._scan_number)) + return TriState(get_scan_event_compensation_voltage(self._handle, self._scan_number)) @property def corona(self): - return TriState(get_scan_filter_corona(self._scan_number)) + return TriState(get_scan_filter_corona(self._handle, self._scan_number)) @property def dependent(self): - return TriState(get_scan_filter_dependent(self._scan_number)) + return TriState(get_scan_filter_dependent(self._handle, self._scan_number)) @property def detector(self): - return DetectorType(get_scan_filter_detector(self._scan_number)) + return DetectorType(get_scan_filter_detector(self._handle, self._scan_number)) @property def detector_value(self): - return get_scan_filter_detector_value(self._scan_number) + return get_scan_filter_detector_value(self._handle, self._scan_number) @property def electron_capture_dissociation(self): return TriState(get_scan_filter_electron_capture_dissociation(self._scan_number)) @@ -1427,7 +1452,7 @@ def enhanced(self): return TriState(get_scan_filter_enhanced(self._scan_number)) @property def field_free_region(self): - return FieldFreeRegionType(get_scan_filter_field_free_region(self._scan_number)) + return FieldFreeRegionType(get_scan_filter_field_free_region(self._handle, self._scan_number)) @property def higher_energy_ci_d(self): return TriState(get_scan_filter_higher_energy_cid(self._scan_number)) @@ -1440,7 +1465,7 @@ def is_custom(self): raise NotImplementedError("is_custom") @property def lock(self): - return TriState(get_scan_filter_lock(self._scan_number)) + return TriState(get_scan_filter_lock(self._handle, self._scan_number)) @property def mass_calibrator_count(self): if _IS_SPHINX: return -1 @@ -1492,7 +1517,7 @@ def pulsed_q_dissociation_value(self): return get_scan_filter_pulsed_q_dissociation_value(self._scan_number) @property def scan_data(self): - return ScanDataType(get_scan_filter_scan_data(self._scan_number)) + return ScanDataType(get_scan_filter_scan_data(self._handle, self._scan_number)) @property def scan_type_index(self): if _IS_SPHINX: return -1 @@ -1520,13 +1545,13 @@ def supplemental_activation(self): return TriState(get_scan_filter_supplemental_activation(self._scan_number)) @property def turbo_scan(self): - return TriState(get_scan_filter_turbo_scan(self._scan_number)) + return TriState(get_scan_filter_turbo_scan(self._handle, self._scan_number)) @property def ultra(self): - return TriState(get_scan_filter_ultra(self._scan_number)) + return TriState(get_scan_filter_ultra(self._handle, self._scan_number)) @property def wideband(self): - return TriState(get_scan_filter_wideband(self._scan_number)) + return TriState(get_scan_filter_wideband(self._handle, self._scan_number)) def get_energy_valid(self, index): if _IS_SPHINX: return 0 raise NotImplementedError("get_energy_valid") diff --git a/native_fisher_py/python/native_fisher_py/raw_file.py b/native_fisher_py/python/native_fisher_py/raw_file.py index 5ce1e60..0db0f8b 100644 --- a/native_fisher_py/python/native_fisher_py/raw_file.py +++ b/native_fisher_py/python/native_fisher_py/raw_file.py @@ -1,4 +1,6 @@ import os +import threading +_chdir_lock = threading.Lock() import numpy as np from typing import List, Tuple from .native_fisher_py_backend import * @@ -38,14 +40,21 @@ class RawFile(object): A high-level wrapper to provide a drop-in replacement for fisher_py.RawFile """ def __init__(self, path: str): - """ - Open a Thermo RAW file. - """ self._path = path - if not os.path.isfile(path): + real_path = os.path.realpath(path) + if not os.path.isfile(real_path): raise FileNotFoundError(f'No raw file with path "{path}" found.') - res = open_raw_file(path) - if res != 0: + + dll_dir = os.path.dirname(__file__) + with _chdir_lock: + original_cwd = os.getcwd() + try: + os.chdir(dll_dir) + self._handle = open_raw_file(real_path) + finally: + os.chdir(original_cwd) + + if getattr(self, "_handle", -1) < 0: raise RawFileException(f"Could not open RAW file: {path}") self._is_open = True @@ -58,7 +67,7 @@ def _raw_file_access(self): return self def select_instrument(self, device_type: int, device_number: int): - pass + select_instrument(self._handle, device_type, device_number) def average_scans(self, start, end): return None def average_scans_in_scan_range(self, start, end, options): return None @@ -66,12 +75,15 @@ def average_scans_in_scan_range(self, start, end, options): return None def default_mass_options(self): return MassOptions() def dispose(self): self.close() def get_all_instrument_names_from_instrument_method(self): return [] - def get_instrument_method(self, index): return "" + def get_instrument_method(self, index): + return get_instrument_method(self._handle, index) + def get_instrument_methods_count(self) -> int: + return get_instrument_method_count(self._handle) def get_instrument_type(self): return 0 def get_segment_event_table(self): return [] - def has_instrument_method(self): return False + def has_instrument_method(self): return self.get_instrument_methods_count() > 0 def is_centroid_scan_from_scan_number(self, scan_number): - return is_centroid(scan_number) + return is_centroid(self._handle, scan_number) def refresh_view_of_file(self): pass @property def selected_instrument(self): return 0 @@ -90,38 +102,38 @@ def __repr__(self): @property def path(self) -> str: - return get_path() + return self._path @property def number_of_scans(self) -> int: - return get_num_scans() + return get_num_scans(self._handle) @property def first_scan(self) -> int: - return get_first_scan() + return get_first_scan(self._handle) @property def last_scan(self) -> int: - return get_last_scan() + return get_last_scan(self._handle) @property def file_name(self) -> str: - return get_file_name() + return get_file_name(self._handle) @property def creation_date(self) -> str: - return get_creation_date() + return get_creation_date(self._handle) @property def computer_name(self) -> str: - return get_computer_name() + return get_computer_name(self._handle) @property def creator_id(self) -> str: - return get_creator_id() + return get_creator_id(self._handle) def get_instrument_data(self) -> InstrumentData: - return InstrumentData() + return InstrumentData(self._handle) @property def run_header(self) -> RunHeader: @@ -133,7 +145,7 @@ def run_header_ex(self) -> RunHeaderEx: @property def sample_information(self) -> SampleInformation: - return SampleInformation() + return SampleInformation(self._handle) @property def instrument_selection(self) -> InstrumentSelection: @@ -141,7 +153,7 @@ def instrument_selection(self) -> InstrumentSelection: @property def file_header(self) -> FileHeader: - return FileHeader() + return FileHeader(self._handle) @property def file_error(self) -> FileError: @@ -161,54 +173,66 @@ def include_reference_and_exception_data(self, value: bool): @property def is_open(self) -> bool: - return is_open() + return is_open(self._handle) @property def is_error(self) -> bool: - return is_error() + return is_error(self._handle) @property def in_acquisition(self) -> bool: - return in_acquisition() + return in_acquisition(self._handle) def retention_time_from_scan_number(self, scan_number: int) -> float: - return get_scan_rt(scan_number) + return get_scan_rt(self._handle, scan_number) def scan_number_from_retention_time(self, rt: float) -> int: - return get_scan_number_from_rt(rt) + return get_scan_number_from_rt(self._handle, rt) def get_scan_event_for_scan_number(self, scan_number: int): from .data.classes import ScanEvent - return ScanEvent(scan_number) + return ScanEvent(self._handle, scan_number) def get_status_log_for_retention_time(self, rt: float): from .data.classes import LogEntry scan = self.scan_number_from_retention_time(rt) - return LogEntry(get_status_log_values(scan)) + return LogEntry(get_status_log_values(self._handle, scan)) def get_status_log_for_scan_number(self, scan_number: int): from .data.classes import LogEntry - return LogEntry(get_status_log_values(scan_number)) + return LogEntry(get_status_log_values(self._handle, scan_number)) def get_scan_event_string_for_scan_number(self, scan_number: int): - return get_scan_event_string(scan_number) + return get_scan_event_string(self._handle, scan_number) def get_centroid_stream(self, scan_number: int, include_ref_peaks: bool = False): from .native_fisher_py_backend import get_centroid_stream from .data.classes import CentroidStream - masses, intensities = get_centroid_stream(scan_number, 1000000) - return CentroidStream(masses=masses, intensities=intensities) + import numpy as np + + print(f"Calling get_centroid_stream with handle={self._handle}, scan={scan_number}"); masses, intensities, baselines, noises, charges, bp_noise, bp_res = get_centroid_stream(self._handle, scan_number, 1000000) + + return CentroidStream( + masses=np.array(masses), + intensities=np.array(intensities), + baselines=np.array(baselines), + noises=np.array(noises), + charges=np.array(charges), + base_peak_noise=bp_noise, + base_peak_resolution=bp_res, + scan_number=scan_number + ) def get_segmented_scan_from_scan_number(self, scan_number: int, stats = None): from .native_fisher_py_backend import get_spectrum from .data.classes import SegmentedScan - masses, intensities = get_spectrum(scan_number, 1000000) + masses, intensities = get_spectrum(self._handle, scan_number, 1000000) return SegmentedScan(masses=masses, intensities=intensities) def get_scan_stats_for_scan_number(self, scan_number: int): from .data.classes import ScanStatistics from .native_fisher_py_backend import get_scan_stats - data = get_scan_stats(scan_number) + data = get_scan_stats(self._handle, scan_number) return ScanStatistics( start_time=data[0], low_mass=data[1], @@ -235,7 +259,7 @@ def get_chromatogram_data(self, settings, start_scan, end_scan, tolerance = None starts = [float(r.low) for r in s.mass_ranges] ends = [float(r.high) for r in s.mass_ranges] - times, intensities = get_chromatogram(trace_type, filter_str, starts, ends, start_scan, end_scan, 1000000) + times, intensities = get_chromatogram(self._handle, trace_type, filter_str, starts, ends, start_scan, end_scan, 1000000) all_times.append(times) all_intensities.append(intensities) all_scans.append([]) # Empty scans for now @@ -243,50 +267,50 @@ def get_chromatogram_data(self, settings, start_scan, end_scan, tolerance = None return ChromatogramData(all_times, all_intensities, all_scans) def get_instrument_count_of_type(self, device_type): - return get_instrument_count_of_type(device_type) + return get_instrument_count_of_type(self._handle, device_type) def get_trailer_extra_information(self, scan_number): from .data.classes import LogEntry labels = [h.label for h in self.get_trailer_extra_header_information()] - return LogEntry(get_trailer_extra_values(scan_number), labels) + return LogEntry(get_trailer_extra_values(self._handle, scan_number), labels) def get_trailer_extra_header_information(self): from .data.classes import HeaderItem - return [HeaderItem(h) for h in get_trailer_extra_header()] + return [HeaderItem(h) for h in get_trailer_extra_header(self._handle)] def get_trailer_extra_values(self, scan_number, formatted=False): - return get_trailer_extra_values(scan_number) + return get_trailer_extra_values(self._handle, scan_number) def get_status_log_header_information(self): from .data.classes import HeaderItem - return [HeaderItem(h) for h in get_status_log_header()] + return [HeaderItem(h) for h in get_status_log_header(self._handle)] def get_status_log_values(self, scan_number, formatted=False): from .data.classes import LogEntry labels = [h.label for h in self.get_status_log_header_information()] - return LogEntry(get_status_log_values(scan_number), labels) + return LogEntry(get_status_log_values(self._handle, scan_number), labels) def get_status_log_entries_count(self): - return get_status_log_count() + return get_status_log_count(self._handle) def get_status_log_for_retention_time(self, rt): from .data.classes import LogEntry labels = [h.label for h in self.get_status_log_header_information()] - return LogEntry(get_status_log_values_for_rt(rt), labels) + return LogEntry(get_status_log_values_for_rt(self._handle, rt), labels) def get_tune_data_count(self): - return get_tune_data_count() + return get_tune_data_count(self._handle) def get_tune_data(self, index): return None - def get_filters(self): return get_filters() + def get_filters(self): return get_filters(self._handle) def get_auto_filters(self): return [] def get_filter_for_scan_number(self, scan_number): from .data.classes import ScanFilter - return ScanFilter(scan_number) + return ScanFilter(self._handle, scan_number) def get_scan_events(self, start, end): return [] def get_scan_dependents(self, scan_number, precision): return ScanDependents() @property def has_ms_data(self) -> bool: - return has_ms_data() + return has_ms_data(self._handle) def get_scan_type(self, scan_number: int): return "" @@ -300,7 +324,7 @@ def get_tune_data_header_information(self, index: int): def get_tune_data_values(self, index: int): from .data.classes import TuneDataValues - return TuneDataValues() + return TuneDataValues(self._handle) @property def instrument_methods_count(self) -> int: @@ -308,32 +332,32 @@ def instrument_methods_count(self) -> int: @property def instrument_count(self) -> int: - return get_instrument_count() + return get_instrument_count(self._handle) @property def total_time_min(self) -> float: - return get_end_time() + return get_end_time(self._handle) def get_chromatogram(self, mass: float = 0.0, tolerance: float = 0.0, trace_type: int = 1, ms_filter: str = 'ms') -> Tuple[np.ndarray, np.ndarray]: starts = [mass - tolerance] if mass > 0 else [] ends = [mass + tolerance] if mass > 0 else [] - times, intensities = get_chromatogram(int(trace_type), ms_filter, starts, ends, -1, -1, 1000000) + times, intensities = get_chromatogram(self._handle, int(trace_type), ms_filter, starts, ends, -1, -1, 1000000) return np.array(times), np.array(intensities) def get_averaged_ms2_scans(self, scan_numbers: List[int]) -> Tuple[np.ndarray, np.ndarray, int]: if not scan_numbers: return np.array([]), np.array([]), 0 - masses, intensities = get_averaged_spectrum(scan_numbers, 1000000) + masses, intensities = get_averaged_spectrum(self._handle, scan_numbers, 1000000) return np.array(masses), np.array(intensities), scan_numbers[0] def get_ms1_scan_number_from_retention_time(self, rt: float) -> Tuple[int, float]: - scan_number = get_ms1_scan_number_from_rt(rt) + scan_number = get_ms1_scan_number_from_rt(self._handle, rt) if scan_number < 1: return 0, 0.0 return scan_number, self.retention_time_from_scan_number(scan_number) def get_ms2_scan_number_from_retention_time(self, rt: float, precursor_mz: float = None) -> Tuple[int, float]: pmz = precursor_mz if precursor_mz is not None else 0.0 - scan_number = get_ms2_scan_number_from_rt(rt, pmz, 1.0) + scan_number = get_ms2_scan_number_from_rt(self._handle, rt, pmz, 1.0) if scan_number < 1: return 0, 0.0 return scan_number, self.retention_time_from_scan_number(scan_number) @@ -368,23 +392,23 @@ def ms2_filter_masses(self) -> List[float]: if not hasattr(self, "_ms2_filter_masses_cache"): mass_set = set() for i in range(self.first_scan, self.last_scan + 1): - if get_ms_order(i) == 2: - mass_set.add(get_precursor_mass(i)) + if get_ms_order(self._handle, i) == 2: + mass_set.add(get_precursor_mass(self._handle, i)) self._ms2_filter_masses_cache = sorted(list(mass_set)) return self._ms2_filter_masses_cache def get_precursor_mz(self, scan_number: int) -> float: - return get_precursor_mass(scan_number) + return get_precursor_mass(self._handle, scan_number) def get_scan_from_scan_number(self, scan_number: int): # Use get_centroid_stream to match behavior for parity - masses, intensities = get_centroid_stream(scan_number, 1000000) + masses, intensities, *_ = get_centroid_stream(self._handle, scan_number, 1000000) charges = np.zeros_like(masses) event_str = self.get_scan_event_string_for_scan_number(scan_number) return np.array(masses), np.array(intensities), charges, event_str def get_scan_number_from_retention_time(self, rt: float) -> int: - return get_scan_number_from_rt(rt) + return get_scan_number_from_rt(self._handle, rt) def __enter__(self): return self @@ -393,4 +417,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.close() def close(self): - close_raw_file() + if hasattr(self, "_handle") and self._handle > 0: + close_raw_file(self._handle) + self._handle = -1 diff --git a/native_fisher_py/python/native_fisher_py/utils/gradient.py b/native_fisher_py/python/native_fisher_py/utils/gradient.py new file mode 100644 index 0000000..0156b22 --- /dev/null +++ b/native_fisher_py/python/native_fisher_py/utils/gradient.py @@ -0,0 +1,37 @@ +import re +from typing import List, Tuple + +def parse_vanquish_neo_gradient(method_text: str) -> dict: + """ + Parses Vanquish Neo style pump lines from instrument method text. + Returns a dictionary with 'solvents' and 'gradient'. + """ + results = { + "solvents": {"A": None, "B": None}, + "gradient": [] + } + + # Extract Solvents + # Format: Neo.PumpModule.Pump.%A_Solvent: H2O + a_match = re.search(r'Pump\.%A_Solvent:\s+(.*)', method_text) + if a_match: + results["solvents"]["A"] = a_match.group(1).strip() + + b_match = re.search(r'Pump\.%B_Solvent:\s+(.*)', method_text) + if b_match: + results["solvents"]["B"] = b_match.group(1).strip() + + # Split by time points like "71.800 [min]" + segments = re.split(r'(\d+\.\d+)\s+\[min\]', method_text) + + current_time = None + for part in segments: + if re.match(r'^\d+\.\d+$', part): + current_time = float(part) + elif current_time is not None: + match = re.search(r'Pump\.%B\.Value:\s+(\d+\.\d+)\s+\[%\]', part) + if match: + percent_b = float(match.group(1)) + results["gradient"].append((current_time, percent_b)) + + return results diff --git a/native_fisher_py/src/lib.rs b/native_fisher_py/src/lib.rs index bfd8d6f..4924149 100644 --- a/native_fisher_py/src/lib.rs +++ b/native_fisher_py/src/lib.rs @@ -80,46 +80,46 @@ fn open_raw_file(path: String) -> PyResult { } #[pyfunction] -fn get_num_scans() -> PyResult { +fn get_num_scans(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_num_scans") + let func: Symbol i32> = lib.get(b"get_num_scans") .map_err(|e| PyErr::new::(format!("get function get_num_scans: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_tune_data_count() -> PyResult { +fn get_tune_data_count(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_tune_data_count") + let func: Symbol i32> = lib.get(b"get_tune_data_count") .map_err(|e| PyErr::new::(format!("get function get_tune_data_count: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_scan_rt(scan_number: i32) -> PyResult { +fn get_scan_rt(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_rt") + let func: Symbol f64> = lib.get(b"get_scan_rt") .map_err(|e| PyErr::new::(format!("get function get_scan_rt: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_spectrum(scan_number: i32, max_length: i32) -> PyResult<(Vec, Vec)> { +fn get_spectrum(handle: i32, scan_number: i32, max_length: i32) -> PyResult<(Vec, Vec)> { let lib = get_lib()?; let mut masses = vec![0.0f64; max_length as usize]; let mut intensities = vec![0.0f64; max_length as usize]; unsafe { - let func: Symbol i32> = lib.get(b"get_spectrum") + let func: Symbol i32> = lib.get(b"get_spectrum") .map_err(|e| PyErr::new::(format!("get function get_spectrum: {}", e)))?; - let actual_len = func(scan_number, masses.as_mut_ptr(), intensities.as_mut_ptr(), max_length); + let actual_len = func(handle, scan_number, masses.as_mut_ptr(), intensities.as_mut_ptr(), max_length); if actual_len < 0 { return Err(PyErr::new::("get_spectrum failed")); } @@ -131,173 +131,191 @@ fn get_spectrum(scan_number: i32, max_length: i32) -> PyResult<(Vec, Vec PyResult { +fn is_centroid(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"is_centroid") + let func: Symbol i32> = lib.get(b"is_centroid") .map_err(|e| PyErr::new::(format!("get function is_centroid: {}", e)))?; - Ok(func(scan_number) != 0) + Ok(func(handle, scan_number) != 0) } } #[pyfunction] -fn get_centroid_stream(scan_number: i32, max_length: i32) -> PyResult<(Vec, Vec)> { +fn get_centroid_stream(handle: i32, scan_number: i32, max_length: i32) -> PyResult<(Vec, Vec, Vec, Vec, Vec, f64, f64)> { let lib = get_lib()?; let mut masses = vec![0.0f64; max_length as usize]; let mut intensities = vec![0.0f64; max_length as usize]; - - unsafe { - let func: Symbol i32> = lib.get(b"get_centroid_stream") - .map_err(|e| PyErr::new::(format!("get function get_centroid_stream: {}", e)))?; - let actual_len = func(scan_number, masses.as_mut_ptr(), intensities.as_mut_ptr(), max_length); - if actual_len < 0 { - return Err(PyErr::new::("get_centroid_stream failed")); - } + let mut baselines = vec![0.0f64; max_length as usize]; + let mut noises = vec![0.0f64; max_length as usize]; + let mut charges = vec![0i32; max_length as usize]; + let mut noise_res = vec![0.0f64; 2]; + + unsafe { + let func: Symbol i32> = + lib.get(b"get_centroid_stream_full") + .map_err(|e| PyErr::new::(format!("get function get_centroid_stream_full: {}", e)))?; + + let actual_len = func(handle, scan_number, + masses.as_mut_ptr(), + intensities.as_mut_ptr(), + baselines.as_mut_ptr(), + noises.as_mut_ptr(), + charges.as_mut_ptr(), + noise_res.as_mut_ptr(), + max_length + ); + + if actual_len < 0 { return Err(PyErr::new::("get_centroid_stream_full failed")); } + let final_len = std::cmp::min(actual_len, max_length) as usize; masses.truncate(final_len); intensities.truncate(final_len); - Ok((masses, intensities)) + baselines.truncate(final_len); + noises.truncate(final_len); + charges.truncate(final_len); + + Ok((masses, intensities, baselines, noises, charges, noise_res[0], noise_res[1])) } } #[pyfunction] -fn get_sample_type() -> PyResult { +fn get_sample_type(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_type") + let func: Symbol i32> = lib.get(b"get_sample_type") .map_err(|e| PyErr::new::(format!("get function get_sample_type: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_sample_row_number() -> PyResult { +fn get_sample_row_number(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_row_number") + let func: Symbol i32> = lib.get(b"get_sample_row_number") .map_err(|e| PyErr::new::(format!("get function get_sample_row_number: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_sample_dilution_factor() -> PyResult { +fn get_sample_dilution_factor(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_sample_dilution_factor") + let func: Symbol f64> = lib.get(b"get_sample_dilution_factor") .map_err(|e| PyErr::new::(format!("get function get_sample_dilution_factor: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_first_scan() -> PyResult { +fn get_first_scan(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_first_scan") + let func: Symbol i32> = lib.get(b"get_first_scan") .map_err(|e| PyErr::new::(format!("get function get_first_scan: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_last_scan() -> PyResult { +fn get_last_scan(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_last_scan") + let func: Symbol i32> = lib.get(b"get_last_scan") .map_err(|e| PyErr::new::(format!("get function get_last_scan: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_end_time() -> PyResult { +fn get_end_time(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_end_time") + let func: Symbol f64> = lib.get(b"get_end_time") .map_err(|e| PyErr::new::(format!("get function get_end_time: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_start_time() -> PyResult { +fn get_start_time(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_start_time") + let func: Symbol f64> = lib.get(b"get_start_time") .map_err(|e| PyErr::new::(format!("get function get_start_time: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_mass_resolution() -> PyResult { +fn get_mass_resolution(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_mass_resolution") + let func: Symbol f64> = lib.get(b"get_mass_resolution") .map_err(|e| PyErr::new::(format!("get function get_mass_resolution: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_expected_runtime() -> PyResult { +fn get_expected_runtime(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_expected_runtime") + let func: Symbol f64> = lib.get(b"get_expected_runtime") .map_err(|e| PyErr::new::(format!("get function get_expected_runtime: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_max_integrated_intensity() -> PyResult { +fn get_max_integrated_intensity(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_max_integrated_intensity") + let func: Symbol f64> = lib.get(b"get_max_integrated_intensity") .map_err(|e| PyErr::new::(format!("get function get_max_integrated_intensity: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_max_intensity() -> PyResult { +fn get_max_intensity(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_max_intensity") + let func: Symbol i32> = lib.get(b"get_max_intensity") .map_err(|e| PyErr::new::(format!("get function get_max_intensity: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_low_mass() -> PyResult { +fn get_low_mass(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_low_mass") + let func: Symbol f64> = lib.get(b"get_low_mass") .map_err(|e| PyErr::new::(format!("get function get_low_mass: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_high_mass() -> PyResult { +fn get_high_mass(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_high_mass") + let func: Symbol f64> = lib.get(b"get_high_mass") .map_err(|e| PyErr::new::(format!("get function get_high_mass: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_file_name() -> PyResult { +fn get_file_name(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_file_name") + let func: Symbol i32> = lib.get(b"get_file_name") .map_err(|e| PyErr::new::(format!("get function get_file_name: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_file_name failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -305,13 +323,13 @@ fn get_file_name() -> PyResult { } #[pyfunction] -fn get_path() -> PyResult { +fn get_path(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_path") + let func: Symbol i32> = lib.get(b"get_path") .map_err(|e| PyErr::new::(format!("get function get_path: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_path failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -319,13 +337,13 @@ fn get_path() -> PyResult { } #[pyfunction] -fn get_creation_date() -> PyResult { +fn get_creation_date(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_creation_date") + let func: Symbol i32> = lib.get(b"get_creation_date") .map_err(|e| PyErr::new::(format!("get function get_creation_date: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_creation_date failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -333,13 +351,13 @@ fn get_creation_date() -> PyResult { } #[pyfunction] -fn get_computer_name() -> PyResult { +fn get_computer_name(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_computer_name") + let func: Symbol i32> = lib.get(b"get_computer_name") .map_err(|e| PyErr::new::(format!("get function get_computer_name: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_computer_name failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -347,13 +365,13 @@ fn get_computer_name() -> PyResult { } #[pyfunction] -fn get_creator_id() -> PyResult { +fn get_creator_id(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_creator_id") + let func: Symbol i32> = lib.get(b"get_creator_id") .map_err(|e| PyErr::new::(format!("get function get_creator_id: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_creator_id failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -361,13 +379,13 @@ fn get_creator_id() -> PyResult { } #[pyfunction] -fn get_instrument_model() -> PyResult { +fn get_instrument_model(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_model") + let func: Symbol i32> = lib.get(b"get_instrument_model") .map_err(|e| PyErr::new::(format!("get function get_instrument_model: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_model failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -375,13 +393,13 @@ fn get_instrument_model() -> PyResult { } #[pyfunction] -fn get_instrument_name() -> PyResult { +fn get_instrument_name(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_name") + let func: Symbol i32> = lib.get(b"get_instrument_name") .map_err(|e| PyErr::new::(format!("get function get_instrument_name: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_name failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -389,13 +407,13 @@ fn get_instrument_name() -> PyResult { } #[pyfunction] -fn get_instrument_serial_number() -> PyResult { +fn get_instrument_serial_number(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_serial_number") + let func: Symbol i32> = lib.get(b"get_instrument_serial_number") .map_err(|e| PyErr::new::(format!("get function get_instrument_serial_number: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_serial_number failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -403,13 +421,13 @@ fn get_instrument_serial_number() -> PyResult { } #[pyfunction] -fn get_instrument_software_version() -> PyResult { +fn get_instrument_software_version(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_software_version") + let func: Symbol i32> = lib.get(b"get_instrument_software_version") .map_err(|e| PyErr::new::(format!("get function get_instrument_software_version: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_software_version failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -417,13 +435,13 @@ fn get_instrument_software_version() -> PyResult { } #[pyfunction] -fn get_instrument_hardware_version() -> PyResult { +fn get_instrument_hardware_version(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_hardware_version") + let func: Symbol i32> = lib.get(b"get_instrument_hardware_version") .map_err(|e| PyErr::new::(format!("get function get_instrument_hardware_version: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_hardware_version failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -431,43 +449,43 @@ fn get_instrument_hardware_version() -> PyResult { } #[pyfunction] -fn get_ms_order(scan_number: i32) -> PyResult { +fn get_ms_order(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_ms_order") + let func: Symbol i32> = lib.get(b"get_ms_order") .map_err(|e| PyErr::new::(format!("get function get_ms_order: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_mass_analyzer(scan_number: i32) -> PyResult { +fn get_mass_analyzer(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_mass_analyzer") + let func: Symbol i32> = lib.get(b"get_mass_analyzer") .map_err(|e| PyErr::new::(format!("get function get_mass_analyzer: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_precursor_mass(scan_number: i32) -> PyResult { +fn get_precursor_mass(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_precursor_mass") + let func: Symbol f64> = lib.get(b"get_precursor_mass") .map_err(|e| PyErr::new::(format!("get function get_precursor_mass: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_string(scan_number: i32) -> PyResult { +fn get_scan_event_string(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_event_string") + let func: Symbol i32> = lib.get(b"get_scan_event_string") .map_err(|e| PyErr::new::(format!("get function get_scan_event_string: {}", e)))?; - let actual_len = func(scan_number, buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, scan_number, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_scan_event_string failed")); } @@ -477,13 +495,13 @@ fn get_scan_event_string(scan_number: i32) -> PyResult { } #[pyfunction] -fn get_scan_filter_string(scan_number: i32) -> PyResult { +fn get_scan_filter_string(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_string") + let func: Symbol i32> = lib.get(b"get_scan_filter_string") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_string: {}", e)))?; - let actual_len = func(scan_number, buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, scan_number, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_scan_filter_string failed")); } @@ -493,23 +511,23 @@ fn get_scan_filter_string(scan_number: i32) -> PyResult { } #[pyfunction] -fn get_scan_number_from_rt(rt: f64) -> PyResult { +fn get_scan_number_from_rt(handle: i32, rt: f64) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_number_from_rt") + let func: Symbol i32> = lib.get(b"get_scan_number_from_rt") .map_err(|e| PyErr::new::(format!("get function get_scan_number_from_rt: {}", e)))?; - Ok(func(rt)) + Ok(func(handle, rt)) } } #[pyfunction] -fn get_ms2_filter_masses(max_size: i32) -> PyResult> { +fn get_ms2_filter_masses(handle: i32, max_size: i32) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0.0f64; max_size as usize]; unsafe { - let func: Symbol i32> = lib.get(b"get_ms2_filter_masses") + let func: Symbol i32> = lib.get(b"get_ms2_filter_masses") .map_err(|e| PyErr::new::(format!("get function get_ms2_filter_masses: {}", e)))?; - let count = func(buffer.as_mut_ptr(), max_size); + let count = func(handle, buffer.as_mut_ptr(), max_size); if count < 0 { return Err(PyErr::new::("get_ms2_filter_masses failed")); } @@ -519,29 +537,29 @@ fn get_ms2_filter_masses(max_size: i32) -> PyResult> { } #[pyfunction] -fn get_ms2_scan_number_from_rt(rt: f64, precursor_mz: f64, tolerance_ppm: f64) -> PyResult { +fn get_ms2_scan_number_from_rt(handle: i32, rt: f64, precursor_mz: f64, tolerance_ppm: f64) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_ms2_scan_number_from_rt") + let func: Symbol i32> = lib.get(b"get_ms2_scan_number_from_rt") .map_err(|e| PyErr::new::(format!("get function get_ms2_scan_number_from_rt: {}", e)))?; - let res = func(rt, precursor_mz, tolerance_ppm); + let res = func(handle, rt, precursor_mz, tolerance_ppm); Ok(res) } } #[pyfunction] -fn get_ms1_scan_number_from_rt(rt: f64) -> PyResult { +fn get_ms1_scan_number_from_rt(handle: i32, rt: f64) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_ms1_scan_number_from_rt") + let func: Symbol i32> = lib.get(b"get_ms1_scan_number_from_rt") .map_err(|e| PyErr::new::(format!("get function get_ms1_scan_number_from_rt: {}", e)))?; - let res = func(rt); + let res = func(handle, rt); Ok(res) } } #[pyfunction] -fn get_chromatogram(trace_type: i32, filter: String, mass_ranges_start: Vec, mass_ranges_end: Vec, start_scan: i32, end_scan: i32, max_length: i32) -> PyResult<(Vec, Vec)> { +fn get_chromatogram(handle: i32, trace_type: i32, filter: String, mass_ranges_start: Vec, mass_ranges_end: Vec, start_scan: i32, end_scan: i32, max_length: i32) -> PyResult<(Vec, Vec)> { let lib = get_lib()?; let mut times = vec![0.0f64; max_length as usize]; let mut intensities = vec![0.0f64; max_length as usize]; @@ -552,9 +570,9 @@ fn get_chromatogram(trace_type: i32, filter: String, mass_ranges_start: Vec let c_filter = std::ffi::CString::new(filter).unwrap(); unsafe { - let func: Symbol i32> = lib.get(b"get_chromatogram") + let func: Symbol i32> = lib.get(b"get_chromatogram") .map_err(|e| PyErr::new::(format!("get function get_chromatogram: {}", e)))?; - let count_res = func(trace_type, c_filter.as_ptr(), start_ptr, end_ptr, count, start_scan, end_scan, times.as_mut_ptr(), intensities.as_mut_ptr(), max_length); + let count_res = func(handle, trace_type, c_filter.as_ptr(), start_ptr, end_ptr, count, start_scan, end_scan, times.as_mut_ptr(), intensities.as_mut_ptr(), max_length); if count_res < 0 { return Err(PyErr::new::("get_chromatogram failed")); } @@ -566,13 +584,13 @@ fn get_chromatogram(trace_type: i32, filter: String, mass_ranges_start: Vec } #[pyfunction] -fn get_filters() -> PyResult> { +fn get_filters(handle: i32) -> PyResult> { let lib = get_lib()?; let mut filters = vec![std::ptr::null_mut(); 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_filters") + let func: Symbol i32> = lib.get(b"get_filters") .map_err(|e| PyErr::new::(format!("get function get_filters: {}", e)))?; - let count = func(filters.as_mut_ptr(), 1024); + let count = func(handle, filters.as_mut_ptr(), 1024); if count < 0 { return Err(PyErr::new::("get_filters failed")); } @@ -589,14 +607,14 @@ fn get_filters() -> PyResult> { } #[pyfunction] -fn get_averaged_spectrum(scan_numbers: Vec, max_length: i32) -> PyResult<(Vec, Vec)> { +fn get_averaged_spectrum(handle: i32, scan_numbers: Vec, max_length: i32) -> PyResult<(Vec, Vec)> { let lib = get_lib()?; let mut masses = vec![0.0f64; max_length as usize]; let mut intensities = vec![0.0f64; max_length as usize]; unsafe { - let func: Symbol i32> = lib.get(b"get_averaged_spectrum") + let func: Symbol i32> = lib.get(b"get_averaged_spectrum") .map_err(|e| PyErr::new::(format!("get function get_averaged_spectrum: {}", e)))?; - let count = func(scan_numbers.as_ptr(), scan_numbers.len() as i32, masses.as_mut_ptr(), intensities.as_mut_ptr(), max_length); + let count = func(handle, scan_numbers.as_ptr(), scan_numbers.len() as i32, masses.as_mut_ptr(), intensities.as_mut_ptr(), max_length); if count < 0 { return Err(PyErr::new::("get_averaged_spectrum failed")); } @@ -608,85 +626,85 @@ fn get_averaged_spectrum(scan_numbers: Vec, max_length: i32) -> PyResult<(V } #[pyfunction] -fn get_instrument_count() -> PyResult { +fn get_instrument_count(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_count") + let func: Symbol i32> = lib.get(b"get_instrument_count") .map_err(|e| PyErr::new::(format!("get function get_instrument_count: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_instrument_count_of_type(device_type: i32) -> PyResult { +fn get_instrument_count_of_type(handle: i32, device_type: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_count_of_type") + let func: Symbol i32> = lib.get(b"get_instrument_count_of_type") .map_err(|e| PyErr::new::(format!("get function get_instrument_count_of_type: {}", e)))?; - Ok(func(device_type)) + Ok(func(handle, device_type)) } } #[pyfunction] -fn is_open() -> PyResult { +fn is_open(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"is_open") + let func: Symbol i32> = lib.get(b"is_open") .map_err(|e| PyErr::new::(format!("get function is_open: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn is_error() -> PyResult { +fn is_error(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"is_error") + let func: Symbol i32> = lib.get(b"is_error") .map_err(|e| PyErr::new::(format!("get function is_error: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn in_acquisition() -> PyResult { +fn in_acquisition(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"in_acquisition") + let func: Symbol i32> = lib.get(b"in_acquisition") .map_err(|e| PyErr::new::(format!("get function in_acquisition: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn has_ms_data() -> PyResult { +fn has_ms_data(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"has_ms_data") + let func: Symbol i32> = lib.get(b"has_ms_data") .map_err(|e| PyErr::new::(format!("get function has_ms_data: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn close_raw_file() -> PyResult<()> { +fn close_raw_file(handle: i32) -> PyResult<()> { let lib = get_lib()?; unsafe { - let func: Symbol = lib.get(b"close_raw_file") + let func: Symbol = lib.get(b"close_raw_file") .map_err(|e| PyErr::new::(format!("get function close_raw_file: {}", e)))?; - func(); + func(handle); Ok(()) } } #[pyfunction] -fn get_scan_filter_meta_filters(scan_number: i32) -> PyResult> { +fn get_scan_filter_meta_filters(handle: i32, scan_number: i32) -> PyResult> { let lib = get_lib()?; let mut filters = vec![std::ptr::null_mut(); 32]; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_meta_filters") + let func: Symbol i32> = lib.get(b"get_scan_filter_meta_filters") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_meta_filters: {}", e)))?; - let count = func(scan_number, filters.as_mut_ptr(), 32); + let count = func(handle, scan_number, filters.as_mut_ptr(), 32); if count < 0 { return Err(PyErr::new::("get_scan_filter_meta_filters failed")); } @@ -703,549 +721,549 @@ fn get_scan_filter_meta_filters(scan_number: i32) -> PyResult> { } #[pyfunction] -fn get_scan_filter_field_free_region(scan_number: i32) -> PyResult { +fn get_scan_filter_field_free_region(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_field_free_region") + let func: Symbol i32> = lib.get(b"get_scan_filter_field_free_region") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_field_free_region: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_index_to_multiple_activation_index(scan_number: i32) -> PyResult { +fn get_scan_filter_index_to_multiple_activation_index(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_index_to_multiple_activation_index") + let func: Symbol i32> = lib.get(b"get_scan_filter_index_to_multiple_activation_index") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_index_to_multiple_activation_index: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_compensation_volt_type(scan_number: i32) -> PyResult { +fn get_scan_filter_compensation_volt_type(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_compensation_volt_type") + let func: Symbol i32> = lib.get(b"get_scan_filter_compensation_volt_type") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_compensation_volt_type: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_compensation_voltage_count(scan_number: i32) -> PyResult { +fn get_scan_filter_compensation_voltage_count(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_compensation_voltage_count") + let func: Symbol i32> = lib.get(b"get_scan_filter_compensation_voltage_count") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_compensation_voltage_count: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_electron_capture_dissociation(scan_number: i32) -> PyResult { +fn get_scan_filter_electron_capture_dissociation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_electron_capture_dissociation") + let func: Symbol i32> = lib.get(b"get_scan_filter_electron_capture_dissociation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_electron_capture_dissociation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_electron_capture_dissociation_value(scan_number: i32) -> PyResult { +fn get_scan_filter_electron_capture_dissociation_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_electron_capture_dissociation_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_electron_capture_dissociation_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_electron_capture_dissociation_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_electron_transfer_dissociation(scan_number: i32) -> PyResult { +fn get_scan_filter_electron_transfer_dissociation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_electron_transfer_dissociation") + let func: Symbol i32> = lib.get(b"get_scan_filter_electron_transfer_dissociation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_electron_transfer_dissociation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_electron_transfer_dissociation_value(scan_number: i32) -> PyResult { +fn get_scan_filter_electron_transfer_dissociation_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_electron_transfer_dissociation_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_electron_transfer_dissociation_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_electron_transfer_dissociation_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_enhanced(scan_number: i32) -> PyResult { +fn get_scan_filter_enhanced(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_enhanced") + let func: Symbol i32> = lib.get(b"get_scan_filter_enhanced") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_enhanced: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_higher_energy_cid(scan_number: i32) -> PyResult { +fn get_scan_filter_higher_energy_cid(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_higher_energy_cid") + let func: Symbol i32> = lib.get(b"get_scan_filter_higher_energy_cid") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_higher_energy_cid: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_higher_energy_cid_value(scan_number: i32) -> PyResult { +fn get_scan_filter_higher_energy_cid_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_higher_energy_cid_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_higher_energy_cid_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_higher_energy_cid_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_multiple_photon_dissociation(scan_number: i32) -> PyResult { +fn get_scan_filter_multiple_photon_dissociation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_multiple_photon_dissociation") + let func: Symbol i32> = lib.get(b"get_scan_filter_multiple_photon_dissociation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_multiple_photon_dissociation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_multiple_photon_dissociation_value(scan_number: i32) -> PyResult { +fn get_scan_filter_multiple_photon_dissociation_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_multiple_photon_dissociation_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_multiple_photon_dissociation_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_multiple_photon_dissociation_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_pulsed_q_dissociation(scan_number: i32) -> PyResult { +fn get_scan_filter_pulsed_q_dissociation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_pulsed_q_dissociation") + let func: Symbol i32> = lib.get(b"get_scan_filter_pulsed_q_dissociation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_pulsed_q_dissociation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_pulsed_q_dissociation_value(scan_number: i32) -> PyResult { +fn get_scan_filter_pulsed_q_dissociation_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_pulsed_q_dissociation_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_pulsed_q_dissociation_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_pulsed_q_dissociation_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_source_fragmentation(scan_number: i32) -> PyResult { +fn get_scan_filter_source_fragmentation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation") + let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_source_fragmentation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_source_fragmentation_info_valid(scan_number: i32) -> PyResult { +fn get_scan_filter_source_fragmentation_info_valid(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation_info_valid") + let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation_info_valid") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_source_fragmentation_info_valid: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_source_fragmentation_type(scan_number: i32) -> PyResult { +fn get_scan_filter_source_fragmentation_type(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation_type") + let func: Symbol i32> = lib.get(b"get_scan_filter_source_fragmentation_type") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_source_fragmentation_type: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_source_fragmentation_value(scan_number: i32) -> PyResult { +fn get_scan_filter_source_fragmentation_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_source_fragmentation_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_source_fragmentation_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_source_fragmentation_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_supplemental_activation(scan_number: i32) -> PyResult { +fn get_scan_filter_supplemental_activation(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_supplemental_activation") + let func: Symbol i32> = lib.get(b"get_scan_filter_supplemental_activation") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_supplemental_activation: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_mass_precision(scan_number: i32) -> PyResult { +fn get_scan_filter_mass_precision(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_mass_precision") + let func: Symbol i32> = lib.get(b"get_scan_filter_mass_precision") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_mass_precision: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_multi_notch(scan_number: i32) -> PyResult { +fn get_scan_filter_multi_notch(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_multi_notch") + let func: Symbol i32> = lib.get(b"get_scan_filter_multi_notch") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_multi_notch: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_multiplex(scan_number: i32) -> PyResult { +fn get_scan_filter_multiplex(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_multiplex") + let func: Symbol i32> = lib.get(b"get_scan_filter_multiplex") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_multiplex: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_unique_mass_count(scan_number: i32) -> PyResult { +fn get_scan_filter_unique_mass_count(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_unique_mass_count") + let func: Symbol i32> = lib.get(b"get_scan_filter_unique_mass_count") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_unique_mass_count: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_param_a(scan_number: i32) -> PyResult { +fn get_scan_filter_param_a(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_param_a") + let func: Symbol f64> = lib.get(b"get_scan_filter_param_a") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_param_a: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_param_b(scan_number: i32) -> PyResult { +fn get_scan_filter_param_b(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_param_b") + let func: Symbol f64> = lib.get(b"get_scan_filter_param_b") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_param_b: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_param_f(scan_number: i32) -> PyResult { +fn get_scan_filter_param_f(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_param_f") + let func: Symbol f64> = lib.get(b"get_scan_filter_param_f") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_param_f: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_param_r(scan_number: i32) -> PyResult { +fn get_scan_filter_param_r(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_param_r") + let func: Symbol f64> = lib.get(b"get_scan_filter_param_r") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_param_r: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_param_v(scan_number: i32) -> PyResult { +fn get_scan_filter_param_v(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_param_v") + let func: Symbol f64> = lib.get(b"get_scan_filter_param_v") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_param_v: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } /// Low-level NativeAOT bridge for Thermo Fisher RAW files. #[pyfunction] -fn get_scan_filter_scan_mode(scan_number: i32) -> PyResult { +fn get_scan_filter_scan_mode(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_scan_mode") + let func: Symbol i32> = lib.get(b"get_scan_filter_scan_mode") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_scan_mode: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_accurate_mass(scan_number: i32) -> PyResult { +fn get_scan_filter_accurate_mass(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_accurate_mass") + let func: Symbol i32> = lib.get(b"get_scan_filter_accurate_mass") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_accurate_mass: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_ionization_mode(scan_number: i32) -> PyResult { +fn get_scan_filter_ionization_mode(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_ionization_mode") + let func: Symbol i32> = lib.get(b"get_scan_filter_ionization_mode") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_ionization_mode: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_lock(scan_number: i32) -> PyResult { +fn get_scan_filter_lock(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_lock") + let func: Symbol i32> = lib.get(b"get_scan_filter_lock") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_lock: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_turbo_scan(scan_number: i32) -> PyResult { +fn get_scan_filter_turbo_scan(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_turbo_scan") + let func: Symbol i32> = lib.get(b"get_scan_filter_turbo_scan") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_turbo_scan: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_corona(scan_number: i32) -> PyResult { +fn get_scan_filter_corona(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_corona") + let func: Symbol i32> = lib.get(b"get_scan_filter_corona") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_corona: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_dependent(scan_number: i32) -> PyResult { +fn get_scan_filter_dependent(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_dependent") + let func: Symbol i32> = lib.get(b"get_scan_filter_dependent") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_dependent: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_detector_value(scan_number: i32) -> PyResult { +fn get_scan_filter_detector_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_filter_detector_value") + let func: Symbol f64> = lib.get(b"get_scan_filter_detector_value") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_detector_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_compensation_voltage(scan_number: i32) -> PyResult { +fn get_scan_event_compensation_voltage(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_event_compensation_voltage") + let func: Symbol i32> = lib.get(b"get_scan_event_compensation_voltage") .map_err(|e| PyErr::new::(format!("get function get_scan_event_compensation_voltage: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_compensation_voltage_value(scan_number: i32) -> PyResult { +fn get_scan_event_compensation_voltage_value(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_event_compensation_voltage_value") + let func: Symbol f64> = lib.get(b"get_scan_event_compensation_voltage_value") .map_err(|e| PyErr::new::(format!("get function get_scan_event_compensation_voltage_value: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_ms_order(scan_number: i32) -> PyResult { +fn get_scan_event_ms_order(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_event_ms_order") + let func: Symbol i32> = lib.get(b"get_scan_event_ms_order") .map_err(|e| PyErr::new::(format!("get function get_scan_event_ms_order: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_mass_count(scan_number: i32) -> PyResult { +fn get_scan_event_mass_count(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_event_mass_count") + let func: Symbol i32> = lib.get(b"get_scan_event_mass_count") .map_err(|e| PyErr::new::(format!("get function get_scan_event_mass_count: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_event_precursor_mass(scan_number: i32, index: i32) -> PyResult { +fn get_scan_event_precursor_mass(handle: i32, scan_number: i32, index: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_event_precursor_mass") + let func: Symbol f64> = lib.get(b"get_scan_event_precursor_mass") .map_err(|e| PyErr::new::(format!("get function get_scan_event_precursor_mass: {}", e)))?; - Ok(func(scan_number, index)) + Ok(func(handle, scan_number, index)) } } #[pyfunction] -fn get_scan_event_activation_type(scan_number: i32, index: i32) -> PyResult { +fn get_scan_event_activation_type(handle: i32, scan_number: i32, index: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_event_activation_type") + let func: Symbol i32> = lib.get(b"get_scan_event_activation_type") .map_err(|e| PyErr::new::(format!("get function get_scan_event_activation_type: {}", e)))?; - Ok(func(scan_number, index)) + Ok(func(handle, scan_number, index)) } } #[pyfunction] -fn get_scan_event_collision_energy(scan_number: i32, index: i32) -> PyResult { +fn get_scan_event_collision_energy(handle: i32, scan_number: i32, index: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol f64> = lib.get(b"get_scan_event_collision_energy") + let func: Symbol f64> = lib.get(b"get_scan_event_collision_energy") .map_err(|e| PyErr::new::(format!("get function get_scan_event_collision_energy: {}", e)))?; - Ok(func(scan_number, index)) + Ok(func(handle, scan_number, index)) } } #[pyfunction] -fn get_scan_stats(scan_number: i32) -> PyResult> { +fn get_scan_stats(handle: i32, scan_number: i32) -> PyResult> { let lib = get_lib()?; let mut data = vec![0.0f64; 8]; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_stats") + let func: Symbol i32> = lib.get(b"get_scan_stats") .map_err(|e| PyErr::new::(format!("get function get_scan_stats: {}", e)))?; - let res = func(scan_number, data.as_mut_ptr()); + let res = func(handle, scan_number, data.as_mut_ptr()); if res < 0 { return Err(PyErr::new::("get_scan_stats failed")); } Ok(data) } } #[pyfunction] -fn get_scan_filter_ultra(scan_number: i32) -> PyResult { +fn get_scan_filter_ultra(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_ultra") + let func: Symbol i32> = lib.get(b"get_scan_filter_ultra") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_ultra: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_wideband(scan_number: i32) -> PyResult { +fn get_scan_filter_wideband(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_wideband") + let func: Symbol i32> = lib.get(b"get_scan_filter_wideband") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_wideband: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_polarity(scan_number: i32) -> PyResult { +fn get_scan_filter_polarity(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_polarity") + let func: Symbol i32> = lib.get(b"get_scan_filter_polarity") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_polarity: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_ms_order(scan_number: i32) -> PyResult { +fn get_scan_filter_ms_order(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_ms_order") + let func: Symbol i32> = lib.get(b"get_scan_filter_ms_order") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_ms_order: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_mass_analyzer(scan_number: i32) -> PyResult { +fn get_scan_filter_mass_analyzer(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_mass_analyzer") + let func: Symbol i32> = lib.get(b"get_scan_filter_mass_analyzer") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_mass_analyzer: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_detector(scan_number: i32) -> PyResult { +fn get_scan_filter_detector(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_detector") + let func: Symbol i32> = lib.get(b"get_scan_filter_detector") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_detector: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_scan_filter_scan_data(scan_number: i32) -> PyResult { +fn get_scan_filter_scan_data(handle: i32, scan_number: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_scan_filter_scan_data") + let func: Symbol i32> = lib.get(b"get_scan_filter_scan_data") .map_err(|e| PyErr::new::(format!("get function get_scan_filter_scan_data: {}", e)))?; - Ok(func(scan_number)) + Ok(func(handle, scan_number)) } } #[pyfunction] -fn get_trailer_extra_count() -> PyResult { +fn get_trailer_extra_count(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_trailer_extra_count") + let func: Symbol i32> = lib.get(b"get_trailer_extra_count") .map_err(|e| PyErr::new::(format!("get function get_trailer_extra_count: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_status_log_values(scan_number: i32) -> PyResult> { +fn get_status_log_values(handle: i32, scan_number: i32) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0u8; 8192]; unsafe { - let func: Symbol i32> = lib.get(b"get_status_log_values") + let func: Symbol i32> = lib.get(b"get_status_log_values") .map_err(|e| PyErr::new::(format!("get function get_status_log_values: {}", e)))?; - let res = func(scan_number, buffer.as_mut_ptr(), 8192); + let res = func(handle, scan_number, buffer.as_mut_ptr(), 8192); if res < 0 { return Err(PyErr::new::("get_status_log_values failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); let s = String::from_utf8_lossy(&buffer[..end]); @@ -1254,13 +1272,13 @@ fn get_status_log_values(scan_number: i32) -> PyResult> { } #[pyfunction] -fn get_status_log_header() -> PyResult> { +fn get_status_log_header(handle: i32) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0u8; 8192]; unsafe { - let func: Symbol i32> = lib.get(b"get_status_log_header") + let func: Symbol i32> = lib.get(b"get_status_log_header") .map_err(|e| PyErr::new::(format!("get function get_status_log_header: {}", e)))?; - let res = func(buffer.as_mut_ptr(), 8192); + let res = func(handle, buffer.as_mut_ptr(), 8192); if res < 0 { return Err(PyErr::new::("get_status_log_header failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); let s = String::from_utf8_lossy(&buffer[..end]); @@ -1269,13 +1287,13 @@ fn get_status_log_header() -> PyResult> { } #[pyfunction] -fn get_status_log_values_for_rt(rt: f64) -> PyResult> { +fn get_status_log_values_for_rt(handle: i32, rt: f64) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0u8; 16384]; unsafe { - let func: Symbol i32> = lib.get(b"get_status_log_values_for_rt") + let func: Symbol i32> = lib.get(b"get_status_log_values_for_rt") .map_err(|e| PyErr::new::(format!("get function get_status_log_values_for_rt: {}", e)))?; - let res = func(rt, buffer.as_mut_ptr(), 16384); + let res = func(handle, rt, buffer.as_mut_ptr(), 16384); if res < 0 { return Err(PyErr::new::("get_status_log_values_for_rt failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); let s = String::from_utf8_lossy(&buffer[..end]); @@ -1284,23 +1302,23 @@ fn get_status_log_values_for_rt(rt: f64) -> PyResult> { } #[pyfunction] -fn get_status_log_count() -> PyResult { +fn get_status_log_count(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_status_log_count") + let func: Symbol i32> = lib.get(b"get_status_log_count") .map_err(|e| PyErr::new::(format!("get function get_status_log_count: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_trailer_extra_values(scan_number: i32) -> PyResult> { +fn get_trailer_extra_values(handle: i32, scan_number: i32) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0u8; 8192]; unsafe { - let func: Symbol i32> = lib.get(b"get_trailer_extra_values") + let func: Symbol i32> = lib.get(b"get_trailer_extra_values") .map_err(|e| PyErr::new::(format!("get function get_trailer_extra_values: {}", e)))?; - let res = func(scan_number, buffer.as_mut_ptr(), 8192); + let res = func(handle, scan_number, buffer.as_mut_ptr(), 8192); if res < 0 { return Err(PyErr::new::("get_trailer_extra_values failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); let s = String::from_utf8_lossy(&buffer[..end]); @@ -1309,13 +1327,13 @@ fn get_trailer_extra_values(scan_number: i32) -> PyResult> { } #[pyfunction] -fn get_trailer_extra_header() -> PyResult> { +fn get_trailer_extra_header(handle: i32) -> PyResult> { let lib = get_lib()?; let mut buffer = vec![0u8; 8192]; unsafe { - let func: Symbol i32> = lib.get(b"get_trailer_extra_header") + let func: Symbol i32> = lib.get(b"get_trailer_extra_header") .map_err(|e| PyErr::new::(format!("get function get_trailer_extra_header: {}", e)))?; - let res = func(buffer.as_mut_ptr(), 8192); + let res = func(handle, buffer.as_mut_ptr(), 8192); if res < 0 { return Err(PyErr::new::("get_trailer_extra_header failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); let s = String::from_utf8_lossy(&buffer[..end]); @@ -1324,13 +1342,13 @@ fn get_trailer_extra_header() -> PyResult> { } #[pyfunction] -fn get_file_description() -> PyResult { +fn get_file_description(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_file_description") + let func: Symbol i32> = lib.get(b"get_file_description") .map_err(|e| PyErr::new::(format!("get function get_file_description: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_file_description failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1338,13 +1356,13 @@ fn get_file_description() -> PyResult { } #[pyfunction] -fn get_modified_date() -> PyResult { +fn get_modified_date(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_modified_date") + let func: Symbol i32> = lib.get(b"get_modified_date") .map_err(|e| PyErr::new::(format!("get function get_modified_date: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_modified_date failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1352,13 +1370,13 @@ fn get_modified_date() -> PyResult { } #[pyfunction] -fn get_who_created_logon() -> PyResult { +fn get_who_created_logon(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_who_created_logon") + let func: Symbol i32> = lib.get(b"get_who_created_logon") .map_err(|e| PyErr::new::(format!("get function get_who_created_logon: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_who_created_logon failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1366,13 +1384,13 @@ fn get_who_created_logon() -> PyResult { } #[pyfunction] -fn get_who_modified_id() -> PyResult { +fn get_who_modified_id(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_who_modified_id") + let func: Symbol i32> = lib.get(b"get_who_modified_id") .map_err(|e| PyErr::new::(format!("get function get_who_modified_id: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_who_modified_id failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1380,13 +1398,13 @@ fn get_who_modified_id() -> PyResult { } #[pyfunction] -fn get_who_modified_logon() -> PyResult { +fn get_who_modified_logon(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_who_modified_logon") + let func: Symbol i32> = lib.get(b"get_who_modified_logon") .map_err(|e| PyErr::new::(format!("get function get_who_modified_logon: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_who_modified_logon failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1394,13 +1412,13 @@ fn get_who_modified_logon() -> PyResult { } #[pyfunction] -fn get_sample_barcode() -> PyResult { +fn get_sample_barcode(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_barcode") + let func: Symbol i32> = lib.get(b"get_sample_barcode") .map_err(|e| PyErr::new::(format!("get function get_sample_barcode: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_sample_barcode failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1408,13 +1426,13 @@ fn get_sample_barcode() -> PyResult { } #[pyfunction] -fn get_sample_id() -> PyResult { +fn get_sample_id(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_id") + let func: Symbol i32> = lib.get(b"get_sample_id") .map_err(|e| PyErr::new::(format!("get function get_sample_id: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_sample_id failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1422,13 +1440,13 @@ fn get_sample_id() -> PyResult { } #[pyfunction] -fn get_sample_name() -> PyResult { +fn get_sample_name(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_name") + let func: Symbol i32> = lib.get(b"get_sample_name") .map_err(|e| PyErr::new::(format!("get function get_sample_name: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_sample_name failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1436,13 +1454,13 @@ fn get_sample_name() -> PyResult { } #[pyfunction] -fn get_sample_vial() -> PyResult { +fn get_sample_vial(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 256]; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_vial") + let func: Symbol i32> = lib.get(b"get_sample_vial") .map_err(|e| PyErr::new::(format!("get function get_sample_vial: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 256); + let actual_len = func(handle, buffer.as_mut_ptr(), 256); if actual_len < 0 { return Err(PyErr::new::("get_sample_vial failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1450,13 +1468,13 @@ fn get_sample_vial() -> PyResult { } #[pyfunction] -fn get_sample_comment() -> PyResult { +fn get_sample_comment(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_sample_comment") + let func: Symbol i32> = lib.get(b"get_sample_comment") .map_err(|e| PyErr::new::(format!("get function get_sample_comment: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_sample_comment failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1464,13 +1482,13 @@ fn get_sample_comment() -> PyResult { } #[pyfunction] -fn get_instrument_axis_label_x() -> PyResult { +fn get_instrument_axis_label_x(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_axis_label_x") + let func: Symbol i32> = lib.get(b"get_instrument_axis_label_x") .map_err(|e| PyErr::new::(format!("get function get_instrument_axis_label_x: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_axis_label_x failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1478,13 +1496,13 @@ fn get_instrument_axis_label_x() -> PyResult { } #[pyfunction] -fn get_instrument_axis_label_y() -> PyResult { +fn get_instrument_axis_label_y(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_axis_label_y") + let func: Symbol i32> = lib.get(b"get_instrument_axis_label_y") .map_err(|e| PyErr::new::(format!("get function get_instrument_axis_label_y: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_axis_label_y failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1492,13 +1510,13 @@ fn get_instrument_axis_label_y() -> PyResult { } #[pyfunction] -fn get_instrument_flags() -> PyResult { +fn get_instrument_flags(handle: i32) -> PyResult { let lib = get_lib()?; let mut buffer = vec![0u8; 1024]; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_flags") + let func: Symbol i32> = lib.get(b"get_instrument_flags") .map_err(|e| PyErr::new::(format!("get function get_instrument_flags: {}", e)))?; - let actual_len = func(buffer.as_mut_ptr(), 1024); + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); if actual_len < 0 { return Err(PyErr::new::("get_instrument_flags failed")); } let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) @@ -1506,44 +1524,178 @@ fn get_instrument_flags() -> PyResult { } #[pyfunction] -fn get_instrument_units() -> PyResult { +fn get_instrument_units(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_units") + let func: Symbol i32> = lib.get(b"get_instrument_units") .map_err(|e| PyErr::new::(format!("get function get_instrument_units: {}", e)))?; - Ok(func()) + Ok(func(handle)) } } #[pyfunction] -fn get_instrument_is_valid() -> PyResult { +fn get_instrument_is_valid(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_is_valid") + let func: Symbol i32> = lib.get(b"get_instrument_is_valid") .map_err(|e| PyErr::new::(format!("get function get_instrument_is_valid: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn get_instrument_has_accurate_mass_precursors() -> PyResult { +fn get_instrument_has_accurate_mass_precursors(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_has_accurate_mass_precursors") + let func: Symbol i32> = lib.get(b"get_instrument_has_accurate_mass_precursors") .map_err(|e| PyErr::new::(format!("get function get_instrument_has_accurate_mass_precursors: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) } } #[pyfunction] -fn get_instrument_is_tsq_quantum_file() -> PyResult { +fn get_instrument_is_tsq_quantum_file(handle: i32) -> PyResult { let lib = get_lib()?; unsafe { - let func: Symbol i32> = lib.get(b"get_instrument_is_tsq_quantum_file") + let func: Symbol i32> = lib.get(b"get_instrument_is_tsq_quantum_file") .map_err(|e| PyErr::new::(format!("get function get_instrument_is_tsq_quantum_file: {}", e)))?; - Ok(func() != 0) + Ok(func(handle) != 0) + } +} + +#[pyfunction] +fn select_instrument(handle: i32, device_type: i32, device_number: i32) -> PyResult<()> { + let lib = get_lib()?; + unsafe { + let func: Symbol = lib.get(b"select_instrument") + .map_err(|e| PyErr::new::(format!("get function select_instrument: {}", e)))?; + func(handle, device_type, device_number); + Ok(()) + } +} + +#[pyfunction] +fn get_instrument_method_count(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_instrument_method_count") + .map_err(|e| PyErr::new::(format!("get function get_instrument_method_count: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_instrument_method(handle: i32, index: i32) -> PyResult { + let lib = get_lib()?; + let mut buffer = vec![0u8; 128 * 1024]; // 128KB buffer for method text + unsafe { + let func: Symbol i32> = lib.get(b"get_instrument_method") + .map_err(|e| PyErr::new::(format!("get function get_instrument_method: {}", e)))?; + let actual_len = func(handle, index, buffer.as_mut_ptr(), 128 * 1024); + if actual_len < 0 { return Err(PyErr::new::("get_instrument_method failed")); } + let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); + Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) + } +} + +#[pyfunction] +fn get_autosampler_tray_index(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_tray_index") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_tray_index: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_autosampler_vial_index(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_vial_index") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_vial_index: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_autosampler_tray_name(handle: i32) -> PyResult { + let lib = get_lib()?; + let mut buffer = vec![0u8; 1024]; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_tray_name") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_tray_name: {}", e)))?; + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); + if actual_len < 0 { return Err(PyErr::new::("get_autosampler_tray_name failed")); } + let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); + Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) + } +} + +#[pyfunction] +fn get_autosampler_tray_shape(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_tray_shape") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_tray_shape: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_autosampler_vials_per_tray(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_vials_per_tray") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_vials_per_tray: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_autosampler_vials_per_tray_x(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_vials_per_tray_x") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_vials_per_tray_x: {}", e)))?; + Ok(func(handle)) } } + +#[pyfunction] +fn get_autosampler_vials_per_tray_y(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol i32> = lib.get(b"get_autosampler_vials_per_tray_y") + .map_err(|e| PyErr::new::(format!("get function get_autosampler_vials_per_tray_y: {}", e)))?; + Ok(func(handle)) + } +} + +#[pyfunction] +fn get_sample_instrument_method_file(handle: i32) -> PyResult { + let lib = get_lib()?; + let mut buffer = vec![0u8; 1024]; + unsafe { + let func: Symbol i32> = lib.get(b"get_sample_instrument_method_file") + .map_err(|e| PyErr::new::(format!("get function get_sample_instrument_method_file: {}", e)))?; + let actual_len = func(handle, buffer.as_mut_ptr(), 1024); + if actual_len < 0 { return Err(PyErr::new::("get_sample_instrument_method_file failed")); } + let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len()); + Ok(String::from_utf8_lossy(&buffer[..end]).into_owned()) + } +} + +#[pyfunction] +fn get_sample_injection_volume(handle: i32) -> PyResult { + let lib = get_lib()?; + unsafe { + let func: Symbol f64> = lib.get(b"get_sample_injection_volume") + .map_err(|e| PyErr::new::(format!("get function get_sample_injection_volume: {}", e)))?; + Ok(func(handle)) + } +} + #[pymodule] fn native_fisher_py_backend(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(open_raw_file, m)?)?; @@ -1565,6 +1717,7 @@ fn native_fisher_py_backend(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_low_mass, m)?)?; m.add_function(wrap_pyfunction!(get_high_mass, m)?)?; m.add_function(wrap_pyfunction!(get_file_name, m)?)?; + m.add_function(wrap_pyfunction!(get_path, m)?)?; m.add_function(wrap_pyfunction!(get_ms_order, m)?)?; m.add_function(wrap_pyfunction!(get_mass_analyzer, m)?)?; m.add_function(wrap_pyfunction!(get_precursor_mass, m)?)?; @@ -1588,6 +1741,8 @@ fn native_fisher_py_backend(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_creator_id, m)?)?; m.add_function(wrap_pyfunction!(get_sample_type, m)?)?; m.add_function(wrap_pyfunction!(get_sample_row_number, m)?)?; + m.add_function(wrap_pyfunction!(get_sample_instrument_method_file, m)?)?; + m.add_function(wrap_pyfunction!(get_sample_injection_volume, m)?)?; m.add_function(wrap_pyfunction!(get_sample_dilution_factor, m)?)?; m.add_function(wrap_pyfunction!(get_instrument_model, m)?)?; m.add_function(wrap_pyfunction!(get_instrument_name, m)?)?; @@ -1671,6 +1826,16 @@ fn native_fisher_py_backend(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(get_scan_filter_meta_filters, m)?)?; m.add_function(wrap_pyfunction!(get_scan_filter_field_free_region, m)?)?; m.add_function(wrap_pyfunction!(get_scan_filter_index_to_multiple_activation_index, m)?)?; + m.add_function(wrap_pyfunction!(get_instrument_method_count, m)?)?; + m.add_function(wrap_pyfunction!(get_instrument_method, m)?)?; + m.add_function(wrap_pyfunction!(select_instrument, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_tray_index, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_vial_index, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_tray_name, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_tray_shape, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_vials_per_tray, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_vials_per_tray_x, m)?)?; + m.add_function(wrap_pyfunction!(get_autosampler_vials_per_tray_y, m)?)?; m.add_function(wrap_pyfunction!(close_raw_file, m)?)?; Ok(()) } diff --git a/test_data/MS2_MS1_zoom.raw.gz b/test_data/MS2_MS1_zoom.raw.gz new file mode 100644 index 0000000..3b96f8b Binary files /dev/null and b/test_data/MS2_MS1_zoom.raw.gz differ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..8e19b6a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,16 @@ +import pytest +import os +from native_fisher_py.raw_file import RawFile + +@pytest.fixture(scope="session") +def zoom_raw_path(): + path = os.path.join("test_data", "MS2_MS1_zoom.raw") + if not os.path.exists(path): + raise FileNotFoundError(f"Test file {path} not found") + return path + +@pytest.fixture(scope="session") +def zoom_raw_file(zoom_raw_path): + raw = RawFile(zoom_raw_path) + yield raw + raw.close() diff --git a/tests/test_centroid.py b/tests/test_centroid.py new file mode 100644 index 0000000..e2c8813 --- /dev/null +++ b/tests/test_centroid.py @@ -0,0 +1,39 @@ +import pytest +import numpy as np + +def test_centroid_stream_retrieval(zoom_raw_file): + # Scan 1 is usually a good starting point + scan_number = 1 + cs = zoom_raw_file.get_centroid_stream(scan_number) + + assert cs is not None + assert cs.scan_number == scan_number + assert isinstance(cs.masses, np.ndarray) + assert isinstance(cs.intensities, np.ndarray) + assert len(cs.masses) == len(cs.intensities) + + if len(cs.masses) > 0: + assert cs.masses[0] > 0 + assert cs.intensities[0] >= 0 + assert cs.base_peak_intensity == np.max(cs.intensities) + assert cs.sum_intensities == np.sum(cs.intensities) + +def test_centroid_stream_extras(zoom_raw_file): + scan_number = 1 + cs = zoom_raw_file.get_centroid_stream(scan_number) + + # Extra data might be zeros if not available, but should be returned as arrays + assert isinstance(cs.baselines, np.ndarray) + assert isinstance(cs.noises, np.ndarray) + assert isinstance(cs.charges, np.ndarray) + + assert len(cs.baselines) == len(cs.masses) + assert len(cs.noises) == len(cs.masses) + assert len(cs.charges) == len(cs.masses) + +def test_is_centroid_scan(zoom_raw_file): + # Test a few scans to see if they are correctly identified + # Using small numbers to avoid potential instability in large scan ranges + for i in range(1, 5): + is_c = zoom_raw_file.is_centroid_scan_from_scan_number(i) + assert isinstance(is_c, bool) diff --git a/tests/test_collision_energy.py b/tests/test_collision_energy.py new file mode 100644 index 0000000..cee3b4f --- /dev/null +++ b/tests/test_collision_energy.py @@ -0,0 +1,38 @@ +import pytest +import os +from native_fisher_py.raw_file import RawFile + +@pytest.fixture(scope="session") +def raw_path(): + return os.path.join("test_data", "MS2_MS1_orbitrap.raw") + +@pytest.fixture(scope="session") +def raw_file(raw_path): + if not os.path.exists(raw_path): + pytest.skip(f"Test file {raw_path} not found") + + raw = RawFile(raw_path) + yield raw + raw.close() + +def test_collision_energy(raw_file): + # Test values obtained from a reference run + energy_expectations = { + 2: 28.0, + 3: 30.0, + 4: 30.0, + 6: 30.0, + 8: 30.0 + } + + for scan_num, expected_energy in energy_expectations.items(): + scan_event = raw_file.get_scan_event_for_scan_number(scan_num) + actual_energy = scan_event.get_energy(0) + assert actual_energy == pytest.approx(expected_energy), f"Scan {scan_num} energy mismatch" + +def test_reaction_collision_energy(raw_file): + # Check first reaction specifically + scan_num = 2 + scan_event = raw_file.get_scan_event_for_scan_number(scan_num) + reaction = scan_event.get_reaction(0) + assert reaction.collision_energy == pytest.approx(28.0) diff --git a/tests/test_gradient.py b/tests/test_gradient.py new file mode 100644 index 0000000..b4d83ac --- /dev/null +++ b/tests/test_gradient.py @@ -0,0 +1,38 @@ +import pytest +from native_fisher_py.utils.gradient import parse_vanquish_neo_gradient + +def test_extract_gradient_from_zoom_file(zoom_raw_file): + # Get the number of methods + method_count = zoom_raw_file.get_instrument_methods_count() + assert method_count > 0, "No instrument methods found" + + result = None + for i in range(method_count): + method_text = zoom_raw_file.get_instrument_method(i) + result = parse_vanquish_neo_gradient(method_text) + if result["gradient"]: + break + + assert result is not None, "Could not extract result from any instrument method" + gradient = result["gradient"] + solvents = result["solvents"] + + assert len(gradient) > 0, "Could not extract gradient" + assert solvents["A"] == "H2O" + assert solvents["B"] == "ACN80" + + # Expected points for 300SPD method + expected_gradient = [ + (0.0, 6.0), + (0.0, 6.0), + (0.2, 10.0), + (2.7, 28.0), + (3.0, 55.0), + (3.1, 99.0), + (3.4, 99.0) + ] + + assert len(gradient) == len(expected_gradient) + for i, (time, b) in enumerate(expected_gradient): + assert gradient[i][0] == pytest.approx(time) + assert gradient[i][1] == pytest.approx(b) diff --git a/tests/test_metadata.py b/tests/test_metadata.py new file mode 100644 index 0000000..10758eb --- /dev/null +++ b/tests/test_metadata.py @@ -0,0 +1,14 @@ +import pytest +from native_fisher_py.data.classes import TrayShape + +def test_metadata_from_zoom_file(zoom_raw_file): + si = zoom_raw_file.sample_information + ai = si.autosampler_information + + assert si.injection_volume == pytest.approx(1.0) + assert si.instrument_method_file.endswith(".meth") + + assert ai.tray_name == "R" + assert ai.tray_index == -1 + assert ai.tray_shape == TrayShape.Circular + assert ai.vial_index == -1 diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..e54860b --- /dev/null +++ b/todo.md @@ -0,0 +1,63 @@ +# Unimplemented API Endpoints + +This document lists the API endpoints and properties that are defined in the `native-fisher-py` codebase but currently lack implementation. +They are ranked by importance for typical mass spectrometry data processing workflows. + +## 1. Critical Importance (Core Data & MS/MS) +*Missing foundational data required for standard LC-MS/MS analysis.* + +| Class | Method / Property | Status | +| :--- | :--- | :--- | +| `ScanEvent` | `get_isolation_width(index)` | ❌ Missing in Native & Python | +| `ScanEvent` | `get_mass_range(index)` | ❌ Missing in Native & Python | +| `Reaction` | `isolation_width` | ❌ Python Placeholder | +| `Reaction` | `isolation_width_offset` | ❌ Python Placeholder | +| `Reaction` | `precursor_range_is_valid` | ❌ Python Placeholder | +| `ScanDependentDetails` | `precursor_mass_array` | ❌ Python Placeholder | +| `ScanDependentDetails` | `isolation_width_array` | ❌ Python Placeholder | + +## 2. High Importance (Metadata & Run Stats) +*Essential for file overview and scan tracking.* + +| Class | Method / Property | Status | +| :--- | :--- | :--- | +| `RunHeader` | `end_time` | ❌ Python Placeholder | +| `RunHeader` | `expected_runtime` | ❌ Python Placeholder | +| `RunHeader` | `high_mass` / `low_mass` | ❌ Python Placeholder | +| `RunHeader` | `spectra_count` | ⚠️ Partially implemented in some subclasses | +| `ScanStatistics` | `cycle_number` | ❌ Python Placeholder | +| `ScanStatistics` | `segment_number` | ❌ Python Placeholder | +| `ScanStatistics` | `scan_type` | ❌ Python Placeholder | +| `ScanStatistics` | `spectrum_packet_type` | ❌ Python Placeholder | + +## 3. Medium Importance (Diagnostic & Advanced Info) +*Useful for peak quality assessment and instrument status.* + +| Class | Method / Property | Status | +| :--- | :--- | :--- | +| `CentroidStream` | `base_peak_noise` | ✅ Implemented | +| `CentroidStream` | `base_peak_resolution` | ✅ Implemented | +| `CentroidStream` | `baselines` | ✅ Implemented | +| `CentroidStream` | `charges` | ✅ Implemented | +| `CentroidStream` | `noises` | ✅ Implemented | +| `SampleInformation` | `injection_volume` | ✅ Implemented | +| `SampleInformation` | `instrument_method_file` | ✅ Implemented | +| `AutoSamplerInformation`| `tray_index` / `vial_index` | ✅ Implemented | +| `AutoSamplerInformation`| `tray_name` / `tray_shape` | ✅ Implemented | + +## 4. Low Importance (Verbose Metadata) +*Auxiliary information rarely used in downstream analysis.* + +| Class | Method / Property | Status | +| :--- | :--- | :--- | +| `ErrorLogEntry` | `message` | ❌ Python Placeholder | +| `ErrorLogEntry` | `retention_time` | ❌ Python Placeholder | +| `HeaderItem` | `is_numeric` | ❌ Python Placeholder | +| `HeaderItem` | `format_value` | ❌ Python Placeholder | +| `FileHeader` | `revision` | ❌ Python Placeholder | +| `FileHeader` | `number_of_times_calibrated`| ❌ Python Placeholder | +| `ScanEvent` | `is_custom` | ❌ Python Placeholder | +| `ScanEvents` | `get_event_by_segment` | ❌ Python Placeholder | + +--- +**Note:** Many Python placeholders return default values when `_IS_SPHINX` is true to allow documentation builds, but will raise `NotImplementedError` during actual execution.