347 lines
16 KiB
C#
347 lines
16 KiB
C#
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<double> 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<double> 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<byte> 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;
|
|
}
|
|
|
|
} |