namespace Helpers.DAQmx; #pragma warning disable IDE1006 // Naming Styles #pragma warning disable IDE0044 // Add readonly modifier #pragma warning disable IDE0051 // Remove unused private members #pragma warning disable CS0169 // Field is never assigned to, and will always have its default value public class DAQmxTask { private IntPtr taskHandle; private DAQmxTask() { } public double DT { get; private set; } public int Channels { get; private set; } public ulong TotalSamplesRead { get; private set; } public DateTime TaskStartedUtc { get; private set; } public ulong TotalSamplesWrite { get; private set; } public ulong TotalSamplesPerChanRead { get; private set; } public ulong TotalSamplesPerChanWrite { get; private set; } public static DAQmxTask Create(string taskName) { DAQmxTask result; IntPtr taskHandle; int task = Interop.DAQmxCreateTask(taskName, out taskHandle); if (task < 0) throw new DAQmxException(task, "Could not create Task"); result = new() { taskHandle = taskHandle, Channels = 0 }; return result; } public void ConfigureLoggingTechnicalDataManagementStreaming(string filePath, DAQmxLoggingMode loggingMode, string groupName, DAQmxLoggingTDMSOperation operation) { int errorCode = Interop.DAQmxConfigureLogging(taskHandle, filePath, (int)loggingMode, groupName, (int)operation); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not configure technical data management streaming logging"); } public void CreateAnalogInputVoltageChannel(string physicalChannel, string nameToAssignToChannel, DAQmxInputTerminalConfiguration terminalConfig, double minVal, double maxVal, DAQmxUnits units, string customScaleName) { int analogInputVoltageChan = Interop.DAQmxCreateAIVoltageChan(taskHandle, physicalChannel, nameToAssignToChannel, (int)terminalConfig, minVal, maxVal, (int)units, customScaleName); if (analogInputVoltageChan < 0) throw new DAQmxException(analogInputVoltageChan, "Could not create analog input voltage channel"); ++Channels; } public void CreateAnalogOutputVoltageChan(string physicalChannel, string nameToAssignToChannel, double minVal, double maxVal, DAQmxUnits units, string customScaleName) { int analogOutputVoltageChan = Interop.DAQmxCreateAOVoltageChan(taskHandle, physicalChannel, nameToAssignToChannel, minVal, maxVal, (int)units, customScaleName); if (analogOutputVoltageChan < 0) throw new DAQmxException(analogOutputVoltageChan, "Could not create analog output voltage channel"); ++Channels; } public void CreateDigitalOutputChanel(string lines, string nameToAssignToLines, DAQmxLineGrouping lineGrouping) { int doChan = Interop.DAQmxCreateDOChan(taskHandle, lines, nameToAssignToLines, (int)lineGrouping); if (doChan < 0) throw new DAQmxException(doChan, "Could not create digital output channel"); ++Channels; } #if unsafe public unsafe void ReadAnalogF64(int numSamplesPerChan, double timeout, DAQmxFillMode fillMode, Span data) { int arraySizeInSamples = numSamplesPerChan * Channels; if (data.Length < arraySizeInSamples) throw new DAQmxMemoryException("Span length too short. (Span length: " + data.Length.ToString() + ", required length: " + arraySizeInSamples.ToString() + ")"); fixed (double* readArray = &data.GetPinnableReference()) { IntPtr samplesPerChanRead; int errorCode = Interop.DAQmxReadAnalogF64(taskHandle, numSamplesPerChan, timeout, (int)fillMode, readArray, (uint)arraySizeInSamples, out samplesPerChanRead, IntPtr.Zero); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not read samples"); int int32 = samplesPerChanRead.ToInt32(); if (int32 != numSamplesPerChan) throw new DAQmxException("Could not read requested number of samples"); TotalSamplesPerChanRead += (ulong)int32; TotalSamplesRead += (ulong)arraySizeInSamples; } } #endif public double ReadAnalogScalarF64(double timeout) { double result; int errorCode = Interop.DAQmxReadAnalogScalarF64(taskHandle, timeout, out result, IntPtr.Zero); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not read samples"); ++TotalSamplesRead; return result; } #if unsafe public double ReadAnalogF64(double timeout) { double result = DAQmxReadAnalogF64(timeout); ++TotalSamplesRead; } #endif #if unsafe private unsafe double DAQmxReadAnalogF64(double timeout) { double results; int numSamplesPerChan = 1; uint arraySizeInSamples = 1; IntPtr samplesPerChanRead = IntPtr.Zero; double* readArray = stackalloc double[1]; int fillMode = (int)DAQmxFillMode.GroupByChannel; int errorCode = Interop.DAQmxReadAnalogF64(taskHandle, numSamplesPerChan, timeout, fillMode, readArray, arraySizeInSamples, out samplesPerChanRead, IntPtr.Zero); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not read samples"); results = *readArray; return results; } #endif public void Start() { int errorCode = Interop.DAQmxStartTask(taskHandle); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not start Task"); TaskStartedUtc = DateTime.UtcNow; } public void Stop() { int errorCode = Interop.DAQmxStopTask(taskHandle); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not stop Task"); } public void Clear() { int errorCode = Interop.DAQmxClearTask(taskHandle); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not clear Task"); } public void CfgSampleClkTiming(string source, double rate, DAQmxActiveEdge activeEdge, DAQmxSampleMode sampleMode, ulong samplesPerChan) { int errorCode = Interop.DAQmxCfgSampClkTiming(taskHandle, source, rate, (int)activeEdge, (int)sampleMode, samplesPerChan); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgSampleClkTiming failed."); DT = 1.0 / SampleClockRate; } public void DAQmxCfgHandshakingTiming(DAQmxSampleMode sampleMode, ulong samplesPerChan) { int errorCode = Interop.DAQmxCfgHandshakingTiming(taskHandle, (int)sampleMode, samplesPerChan); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgHandshakingTiming failed."); } public void DAQmxCfgBurstHandshakingTimingImportClock(DAQmxSampleMode sampleMode, ulong samplesPerChan, double sampleClkRate, string sampleClkSrc, DAQmxPolarity sampleClkActiveEdge, DAQmxLevel pauseWhen, DAQmxPolarity readyEventActiveLevel) { int errorCode = Interop.DAQmxCfgBurstHandshakingTimingImportClock(taskHandle, (int)sampleMode, samplesPerChan, sampleClkRate, sampleClkSrc, (int)sampleClkActiveEdge, (int)pauseWhen, (int)readyEventActiveLevel); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgBurstHandshakingTimingImportClock failed."); } public void DAQmxCfgBurstHandshakingTimingExportClock(DAQmxSampleMode sampleMode, ulong samplesPerChan, double sampleClkRate, string sampleClkOutputTerm, DAQmxPolarity sampleClkPulsePolarity, DAQmxLevel pauseWhen, DAQmxPolarity readyEventActiveLevel) { int errorCode = Interop.DAQmxCfgBurstHandshakingTimingExportClock(taskHandle, (int)sampleMode, samplesPerChan, sampleClkRate, sampleClkOutputTerm, (int)sampleClkPulsePolarity, (int)pauseWhen, (int)readyEventActiveLevel); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgBurstHandshakingTimingExportClock failed."); } public void DAQmxCfgChangeDetectionTiming(string risingEdgeChan, string fallingEdgeChan, DAQmxSampleMode sampleMode, ulong samplesPerChan) { int errorCode = Interop.DAQmxCfgChangeDetectionTiming(taskHandle, risingEdgeChan, fallingEdgeChan, (int)sampleMode, samplesPerChan); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgChangeDetectionTiming failed."); } public void DAQmxCfgImplicitTiming(DAQmxSampleMode sampleMode, ulong samplesPerChan) { int errorCode = Interop.DAQmxCfgImplicitTiming(taskHandle, (int)sampleMode, samplesPerChan); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgImplicitTiming failed."); } public void DAQmxCfgPipelinedSampleClkTiming(string source, double rate, DAQmxActiveEdge activeEdge, DAQmxSampleMode sampleMode, ulong samplesPerChan) { int errorCode = Interop.DAQmxCfgPipelinedSampClkTiming(taskHandle, source, rate, (int)activeEdge, (int)sampleMode, samplesPerChan); if (errorCode < 0) throw new DAQmxException(errorCode, "DAQmxCfgPipelinedSampleClkTiming failed."); } public double SampleClockRate { get { double result = 0.0; int sampleClkRate = Interop.DAQmxGetSampClkRate(taskHandle, ref result); if (sampleClkRate < 0) throw new DAQmxException(sampleClkRate, "Could not get SampleClockRate"); return result; } set { int errorCode = Interop.DAQmxSetSampClkRate(taskHandle, value); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not set SampleClockRate"); } } public double SampleClockMaxRate { get { double result = 0.0; int sampleClkMaxRate = Interop.DAQmxGetSampClkMaxRate(taskHandle, ref result); if (sampleClkMaxRate < 0) throw new DAQmxException(sampleClkMaxRate, "Could not get SampleClockMaxRate"); return result; } } public void DisableStartTrig() { int errorCode = Interop.DAQmxDisableStartTrig(taskHandle); if (errorCode < 0) throw new DAQmxException(errorCode, "DisableStartTrig failed."); } public void CfgDigEdgeStartTrig(string triggerSource, DAQmxEdge triggerEdge) { int errorCode = Interop.DAQmxCfgDigEdgeStartTrig(taskHandle, triggerSource, (int)triggerEdge); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgDigEdgeStartTrig failed."); } public void CfgAnalogEdgeStartTrig(string triggerSource, DAQmxEdge triggerSlope, double triggerLevel) { int errorCode = Interop.DAQmxCfgAnlgEdgeStartTrig(taskHandle, triggerSource, (int)triggerSlope, triggerLevel); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgAnalogEdgeStartTrig failed."); } public void CfgAnalogMultiEdgeStartTrig(string triggerSources, DAQmxEdge[] triggerSlopes, double[] triggerLevels) => throw new NotImplementedException("CfgAnalogMultiEdgeStartTrig is not implemented in this version."); public void CfgAnalogWindowStartTrig(string triggerSource, DAQmxWindowTriggerWhen triggerWhen, double windowTop, double windowBottom) { int errorCode = Interop.DAQmxCfgAnlgWindowStartTrig(taskHandle, triggerSource, (int)triggerWhen, windowTop, windowBottom); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgAnalogWindowStartTrig failed."); } public void CfgTimeStartTrig(CVIAbsoluteTime when, DAQmxTimescale timescale) { int errorCode = Interop.DAQmxCfgTimeStartTrig(taskHandle, when, (int)timescale); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgTimeStartTrig failed."); } public void CfgDigPatternStartTrig(string triggerSource, string triggerPattern, DAQmxDigitalPatternTriggerWhen triggerWhen) { int errorCode = Interop.DAQmxCfgDigPatternStartTrig(taskHandle, triggerSource, triggerPattern, (int)triggerWhen); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgDigPatternStartTrig failed."); } public void DisableRefTrig() { int errorCode = Interop.DAQmxDisableRefTrig(taskHandle); if (errorCode < 0) throw new DAQmxException(errorCode, "DisableRefTrig failed."); } public void CfgDigEdgeRefTrig(string triggerSource, DAQmxEdge triggerEdge, uint pretriggerSamples) { int errorCode = Interop.DAQmxCfgDigEdgeRefTrig(taskHandle, triggerSource, (int)triggerEdge, pretriggerSamples); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgDigEdgeRefTrig failed."); } public void CfgAnalogEdgeRefTrig(string triggerSource, DAQmxEdge triggerSlope, double triggerLevel, uint pretriggerSamples) { int errorCode = Interop.DAQmxCfgAnlgEdgeRefTrig(taskHandle, triggerSource, (int)triggerSlope, triggerLevel, pretriggerSamples); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgAnalogEdgeRefTrig failed."); } public void CfgAnalogMultiEdgeRefTrig(string triggerSources, DAQmxEdge[] triggerSlopes, double[] triggerLevels, uint pretriggerSamples) => throw new NotImplementedException("CfgAnalogMultiEdgeRefTrig is not implemented in this version."); public void CfgAnalogWindowRefTrig(string triggerSource, DAQmxWindowTriggerWhen triggerWhen, double windowTop, double windowBottom, uint pretriggerSamples) { int errorCode = Interop.DAQmxCfgAnlgWindowRefTrig(taskHandle, triggerSource, (int)triggerWhen, windowTop, windowBottom, pretriggerSamples); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgAnalogWindowRefTrig failed."); } public void CfgDigPatternRefTrig(string triggerSource, string triggerPattern, DAQmxDigitalPatternTriggerWhen triggerWhen, uint pretriggerSamples) { int errorCode = Interop.DAQmxCfgDigPatternRefTrig(taskHandle, triggerSource, triggerPattern, (int)triggerWhen, pretriggerSamples); if (errorCode < 0) throw new DAQmxException(errorCode, "CfgDigPatternRefTrig failed."); } public void WriteAnalogF64(int numSamplesPerChan, bool autoStart, double timeout, DAQmxDataLayout dataLayout, Span data) { int num = numSamplesPerChan * Channels; if (data.Length < num) throw new DAQmxMemoryException("Span length too short. (Span length: " + data.Length.ToString() + ", required length: " + num.ToString() + ")"); IntPtr samplesPerChanWritten; int errorCode = Interop.DAQmxWriteAnalogF64(taskHandle, numSamplesPerChan, autoStart, timeout, dataLayout > DAQmxDataLayout.GroupByChannel, data.ToArray(), out samplesPerChanWritten, IntPtr.Zero); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not write samples"); int int32 = samplesPerChanWritten.ToInt32(); if (int32 != numSamplesPerChan) throw new DAQmxException("Could not write requested number of samples"); TotalSamplesPerChanWrite += (ulong)int32; TotalSamplesWrite += (ulong)num; } public void WriteDigitalLines(int numSamplesPerChan, bool autoStart, double timeout, DAQmxDataLayout dataLayout, Span data) { int num = numSamplesPerChan * Channels; if (data.Length < num) throw new DAQmxMemoryException("Span length too short. (Span length: " + data.Length.ToString() + ", required length: " + num.ToString() + ")"); IntPtr samplesPerChanWritten; int errorCode = Interop.DAQmxWriteDigitalLines(taskHandle, numSamplesPerChan, autoStart, timeout, dataLayout > DAQmxDataLayout.GroupByChannel, data.ToArray(), out samplesPerChanWritten, IntPtr.Zero); if (errorCode < 0) throw new DAQmxException(errorCode, "Could not write samples"); int int32 = samplesPerChanWritten.ToInt32(); if (int32 != numSamplesPerChan) throw new DAQmxException("Could not write requested number of samples"); TotalSamplesPerChanWrite += (ulong)int32; TotalSamplesWrite += (ulong)num; } }