diff --git a/.editorconfig b/.editorconfig
index b48850a..73cf788 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -118,7 +118,7 @@ dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
-dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
+dotnet_diagnostic.IDE0005.severity = none # Using directive is unnecessary
dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010)
dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 775aade..8c66bab 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,7 +8,7 @@
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
- "preLaunchTask": "build",
+ "preLaunchTask": "Build",
"program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Watcher.dll",
"args": [
"s"
diff --git a/.vscode/settings.json b/.vscode/settings.json
index e010ee0..7a59eab 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -42,6 +42,7 @@
"PDSF",
"pged",
"Phares",
+ "Pinnable",
"Rijndael",
"Serilog",
"SUBM",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 6b33062..3ff7286 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -7,8 +7,6 @@
"type": "process",
"args": [
"user-secrets",
- "-p",
- "${workspaceFolder}/File-Watcher.csproj",
"init"
],
"problemMatcher": "$msCompile"
@@ -19,8 +17,6 @@
"type": "process",
"args": [
"user-secrets",
- "-p",
- "${workspaceFolder}/File-Watcher.csproj",
"set",
"_UserSecretsId",
"6516d19d6569"
@@ -43,19 +39,16 @@
"problemMatcher": "$msCompile"
},
{
- "label": "build",
+ "label": "Build",
"command": "dotnet",
"type": "process",
"args": [
- "build",
- "${workspaceFolder}/File-Watcher.csproj",
- "/property:GenerateFullPaths=true",
- "/consoleloggerparameters:NoSummary"
+ "build"
],
"problemMatcher": "$msCompile"
},
{
- "label": "Format-Whitespaces",
+ "label": "Format Whitespaces",
"command": "dotnet",
"type": "process",
"args": [
@@ -74,10 +67,7 @@
"win-x64",
"-c",
"Release",
- "-p:PublishAot=true",
- "${workspaceFolder}/File-Watcher.csproj",
- "/property:GenerateFullPaths=true",
- "/consoleloggerparameters:NoSummary"
+ "-p:PublishAot=true"
],
"problemMatcher": "$msCompile"
},
diff --git a/File-Watcher.csproj b/File-Watcher.csproj
index b96ce9a..96f74e1 100644
--- a/File-Watcher.csproj
+++ b/File-Watcher.csproj
@@ -12,11 +12,6 @@
-
-
-
-
-
@@ -34,4 +29,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Helpers/DAQmx/CSVTime.cs b/Helpers/DAQmx/CSVTime.cs
new file mode 100644
index 0000000..c24bd5e
--- /dev/null
+++ b/Helpers/DAQmx/CSVTime.cs
@@ -0,0 +1,12 @@
+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 struct CSVTime
+{
+ private ulong lsb;
+ private long msb;
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/CVIAbsoluteTime.cs b/Helpers/DAQmx/CVIAbsoluteTime.cs
new file mode 100644
index 0000000..16c5a01
--- /dev/null
+++ b/Helpers/DAQmx/CVIAbsoluteTime.cs
@@ -0,0 +1,17 @@
+using System.Runtime.InteropServices;
+
+namespace Helpers.DAQmx;
+
+#pragma warning disable IDE0044 // Add readonly modifier
+#pragma warning disable IDE0051 // Remove unused private members
+#pragma warning disable IDE1006 // Naming Styles
+#pragma warning disable CS0169 // Field is never assigned to, and will always have its default value
+
+[StructLayout(LayoutKind.Explicit)]
+public struct CVIAbsoluteTime
+{
+ [FieldOffset(0)]
+ private CSVTime cviTime;
+ [FieldOffset(0)]
+ private uint[] u32Data;
+}
diff --git a/Helpers/DAQmx/DAQmx.cs b/Helpers/DAQmx/DAQmx.cs
new file mode 100644
index 0000000..05c5640
--- /dev/null
+++ b/Helpers/DAQmx/DAQmx.cs
@@ -0,0 +1,13 @@
+using System.Text;
+
+namespace Helpers.DAQmx;
+
+public static class DAQmx
+{
+ public static string GetErrorString(int errorCode)
+ {
+ StringBuilder errorString = new(256);
+ _ = Interop.DAQmxGetErrorString(errorCode, errorString, (uint)(errorString.Capacity + 1));
+ return errorString.ToString();
+ }
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxActiveEdge.cs b/Helpers/DAQmx/DAQmxActiveEdge.cs
new file mode 100644
index 0000000..c164502
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxActiveEdge.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxActiveEdge
+{
+ Falling = 10171, // 0x000027BB
+ Rising = 10280, // 0x00002828
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxDataLayout.cs b/Helpers/DAQmx/DAQmxDataLayout.cs
new file mode 100644
index 0000000..c906d17
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxDataLayout.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxDataLayout
+{
+ GroupByChannel,
+ GroupByScanNumber,
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxDigitalPatternTriggerWhen.cs b/Helpers/DAQmx/DAQmxDigitalPatternTriggerWhen.cs
new file mode 100644
index 0000000..156b14c
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxDigitalPatternTriggerWhen.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxDigitalPatternTriggerWhen
+{
+ PatternDoesNotMatch = 10253, // 0x0000280D
+ PatternMatches = 10254, // 0x0000280E
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxEdge.cs b/Helpers/DAQmx/DAQmxEdge.cs
new file mode 100644
index 0000000..182d753
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxEdge.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxEdge
+{
+ Falling = 10171, // 0x000027BB
+ Rising = 10280, // 0x00002828
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxException.cs b/Helpers/DAQmx/DAQmxException.cs
new file mode 100644
index 0000000..cac872e
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxException.cs
@@ -0,0 +1,14 @@
+namespace Helpers.DAQmx;
+
+public class DAQmxException : Exception
+{
+ public DAQmxException(string message)
+ : base(message)
+ {
+ }
+
+ public DAQmxException(int errorCode, string message)
+ : base(errorCode.ToString() + " - " + message + " (" + DAQmx.GetErrorString(errorCode) + ")")
+ {
+ }
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxFillMode.cs b/Helpers/DAQmx/DAQmxFillMode.cs
new file mode 100644
index 0000000..d938d76
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxFillMode.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxFillMode
+{
+ GroupByChannel,
+ GroupByScanNumber,
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxInputTerminalConfiguration.cs b/Helpers/DAQmx/DAQmxInputTerminalConfiguration.cs
new file mode 100644
index 0000000..25ef331
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxInputTerminalConfiguration.cs
@@ -0,0 +1,10 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxInputTerminalConfiguration
+{
+ Default = -1, // 0xFFFFFFFF
+ NonReferencedSingleEnded = 10078, // 0x0000275E
+ ReferencedSingleEnded = 10083, // 0x00002763
+ Differential = 10106, // 0x0000277A
+ Pseudodifferential = 12529, // 0x000030F1
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxLevel.cs b/Helpers/DAQmx/DAQmxLevel.cs
new file mode 100644
index 0000000..5734d65
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxLevel.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxLevel
+{
+ High = 10192, // 0x000027D0
+ Low = 10214, // 0x000027E6
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxLineGrouping.cs b/Helpers/DAQmx/DAQmxLineGrouping.cs
new file mode 100644
index 0000000..b2a6f81
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxLineGrouping.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxLineGrouping
+{
+ OneChannelForEachLine,
+ OneChannelForAllLines,
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxLoggingMode.cs b/Helpers/DAQmx/DAQmxLoggingMode.cs
new file mode 100644
index 0000000..d17218c
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxLoggingMode.cs
@@ -0,0 +1,8 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxLoggingMode
+{
+ Off = 10231, // 0x000027F7
+ LogAndRead = 15842, // 0x00003DE2
+ Log = 15844, // 0x00003DE4
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxLoggingTDMSOperation.cs b/Helpers/DAQmx/DAQmxLoggingTDMSOperation.cs
new file mode 100644
index 0000000..c400825
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxLoggingTDMSOperation.cs
@@ -0,0 +1,9 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxLoggingTDMSOperation
+{
+ Open = 10437, // 0x000028C5
+ OpenOrCreate = 15846, // 0x00003DE6
+ CreateOrReplace = 15847, // 0x00003DE7
+ Create = 15848, // 0x00003DE8
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxMemoryException.cs b/Helpers/DAQmx/DAQmxMemoryException.cs
new file mode 100644
index 0000000..8f503a9
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxMemoryException.cs
@@ -0,0 +1,5 @@
+namespace Helpers.DAQmx;
+
+public class DAQmxMemoryException(string message) : Exception(message)
+{
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxPolarity.cs b/Helpers/DAQmx/DAQmxPolarity.cs
new file mode 100644
index 0000000..c14d506
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxPolarity.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxPolarity
+{
+ ActiveHigh = 10095, // 0x0000276F
+ ActiveLow = 10096, // 0x00002770
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxSampleMode.cs b/Helpers/DAQmx/DAQmxSampleMode.cs
new file mode 100644
index 0000000..864d219
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxSampleMode.cs
@@ -0,0 +1,8 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxSampleMode
+{
+ ContinuousSamples = 10123, // 0x0000278B
+ FiniteSamples = 10178, // 0x000027C2
+ HardwareTimedSinglePoint = 12522, // 0x000030EA
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxTask.cs b/Helpers/DAQmx/DAQmxTask.cs
new file mode 100644
index 0000000..0bed946
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxTask.cs
@@ -0,0 +1,347 @@
+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;
+ }
+
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxTimescale.cs b/Helpers/DAQmx/DAQmxTimescale.cs
new file mode 100644
index 0000000..bb309f9
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxTimescale.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxTimescale
+{
+ HostTime = 16126, // 0x00003EFE
+ IODeviceTime = 16127, // 0x00003EFF
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxUnits.cs b/Helpers/DAQmx/DAQmxUnits.cs
new file mode 100644
index 0000000..6470f12
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxUnits.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxUnits
+{
+ FromCustomScale = 10065, // 0x00002751
+ Volts = 10348, // 0x0000286C
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxWindowTriggerWhen.cs b/Helpers/DAQmx/DAQmxWindowTriggerWhen.cs
new file mode 100644
index 0000000..739272b
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxWindowTriggerWhen.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxWindowTriggerWhen
+{
+ EnteringWindow = 10163, // 0x000027B3
+ LeavingWindow = 10208, // 0x000027E0
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/DAQmxWriteRegenMode.cs b/Helpers/DAQmx/DAQmxWriteRegenMode.cs
new file mode 100644
index 0000000..1eea825
--- /dev/null
+++ b/Helpers/DAQmx/DAQmxWriteRegenMode.cs
@@ -0,0 +1,7 @@
+namespace Helpers.DAQmx;
+
+public enum DAQmxWriteRegenMode
+{
+ AllowRegen = 10097, // 0x00002771
+ DoNotAllowRegen = 10158, // 0x000027AE
+}
\ No newline at end of file
diff --git a/Helpers/DAQmx/Interop.cs b/Helpers/DAQmx/Interop.cs
new file mode 100644
index 0000000..e53ad85
--- /dev/null
+++ b/Helpers/DAQmx/Interop.cs
@@ -0,0 +1,431 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+
+#pragma warning disable IDE1006 // Naming Styles
+#pragma warning disable SYSLIB1054 // Type or member is obsolete
+
+namespace Helpers.DAQmx;
+
+internal class Interop
+{ // cSpell:disable
+
+ private const string lib = "DAQmx";
+ private static IntPtr libHandle = IntPtr.Zero;
+
+ static Interop() =>
+ NativeLibrary.SetDllImportResolver(typeof(Interop).Assembly, ImportResolver);
+
+ private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ if (libraryName == "DAQmx" && !(libHandle != IntPtr.Zero))
+ {
+ bool loaded;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ loaded = NativeLibrary.TryLoad("C:/Windows/System32/nicaiu.dll", assembly, searchPath, out libHandle);
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ loaded = NativeLibrary.TryLoad("/usr/local/lib64/libnidaqmxbase.so", assembly, searchPath, out libHandle);
+ if (!loaded)
+ loaded = NativeLibrary.TryLoad("/usr/local/lib/libnidaqmxbase.so", assembly, searchPath, out libHandle);
+ if (!loaded)
+ loaded = NativeLibrary.TryLoad("/usr/local/natinst/lib/libnidaqmx.so", assembly, searchPath, out libHandle);
+ if (!loaded)
+ loaded = NativeLibrary.TryLoad("/usr/lib/x86_64-linux-gnu/libnidaqmx.so", assembly, searchPath, out libHandle);
+ }
+ else
+ throw new PlatformNotSupportedException("Unsupported platform for DAQmx library.");
+ if (!loaded)
+ throw new DllNotFoundException($"Failed to load {lib} library.");
+ }
+ return libHandle;
+ }
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetSampClkRate(IntPtr taskHandle, ref double data);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxSetSampClkRate(IntPtr taskHandle, double data);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetSampClkMaxRate(IntPtr taskHandle, ref double data);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxSetWriteRegenMode(IntPtr taskHandle, int data);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCreateAIVoltageChan(
+ IntPtr taskHandle,
+ string physicalChannel,
+ string nameToAssignToChannel,
+ int terminalConfig,
+ double minVal,
+ double maxVal,
+ int units,
+ string customScaleName);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCreateAOVoltageChan(
+ IntPtr taskHandle,
+ string physicalChannel,
+ string nameToAssignToChannel,
+ double minVal,
+ double maxVal,
+ int units,
+ string customScaleName);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCreateDOChan(
+ IntPtr taskHandle,
+ string lines,
+ string nameToAssignToLines,
+ int lineGrouping);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxGetErrorString(
+ int errorCode,
+ StringBuilder errorString,
+ uint buffersize);
+
+#if unsafe
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern unsafe int DAQmxReadAnalogF64(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ double timeout,
+ int fillMode,
+ double* readArray,
+ uint arraySizeInSamps,
+ out IntPtr sampsPerChanRead,
+ IntPtr reserved);
+#endif
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxReadAnalogScalarF64(
+ IntPtr taskHandle,
+ double timeout,
+ out double value,
+ IntPtr reserved);
+
+#if unsafe
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern unsafe int DAQmxReadBinaryI16(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ double timeout,
+ int fillMode,
+ bool* readArray,
+ uint arraySizeInSamps,
+ out IntPtr sampsPerChanRead,
+ IntPtr reserved);
+#endif
+
+#if unsafe
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern unsafe int DAQmxReadBinaryU16(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ double timeout,
+ int fillMode,
+ ushort* readArray,
+ uint arraySizeInSamps,
+ out IntPtr sampsPerChanRead,
+ IntPtr reserved);
+#endif
+
+#if unsafe
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern unsafe int DAQmxReadBinaryI32(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ double timeout,
+ int fillMode,
+ int* readArray,
+ uint arraySizeInSamps,
+ out IntPtr sampsPerChanRead,
+ IntPtr reserved);
+#endif
+
+#if unsafe
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern unsafe int DAQmxReadBinaryU32(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ double timeout,
+ int fillMode,
+ uint* readArray,
+ uint arraySizeInSamps,
+ out IntPtr sampsPerChanRead,
+ IntPtr reserved);
+#endif
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxConfigureLogging(
+ IntPtr taskHandle,
+ string filePath,
+ int loggingMode,
+ string groupName,
+ int operation);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxLoadTask(string taskName, out IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCreateTask(string taskName, out IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxAddGlobalChansToTask(IntPtr taskHandle, string[] channelNames);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxStartTask(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxStopTask(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxClearTask(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxWaitUntilTaskDone(IntPtr taskHandle, double timeToWait);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxWaitForValidTimestamp(
+ IntPtr taskHandle,
+ int timestampEvent,
+ double timeout,
+ CVIAbsoluteTime timestamp);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxIsTaskDone(IntPtr taskHandle, out uint isTaskDone);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxTaskControl(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetNthTaskChannel(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetNthTaskDevice(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetTaskAttribute(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgSampClkTiming(
+ IntPtr taskHandle,
+ string source,
+ double rate,
+ int activeEdge,
+ int sampleMode,
+ ulong sampsPerChan);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgHandshakingTiming(
+ IntPtr taskHandle,
+ int sampleMode,
+ ulong sampsPerChan);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgBurstHandshakingTimingImportClock(
+ IntPtr taskHandle,
+ int sampleMode,
+ ulong sampsPerChan,
+ double sampleClkRate,
+ string sampleClkSrc,
+ int sampleClkActiveEdge,
+ int pauseWhen,
+ int readyEventActiveLevel);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgBurstHandshakingTimingExportClock(
+ IntPtr taskHandle,
+ int sampleMode,
+ ulong sampsPerChan,
+ double sampleClkRate,
+ string sampleClkOutpTerm,
+ int sampleClkPulsePolarity,
+ int pauseWhen,
+ int readyEventActiveLevel);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgChangeDetectionTiming(
+ IntPtr taskHandle,
+ string risingEdgeChan,
+ string fallingEdgeChan,
+ int sampleMode,
+ ulong sampsPerChan);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgImplicitTiming(
+ IntPtr taskHandle,
+ int sampleMode,
+ ulong sampsPerChan);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxCfgPipelinedSampClkTiming(
+ IntPtr taskHandle,
+ string source,
+ double rate,
+ int activeEdge,
+ int sampleMode,
+ ulong sampsPerChan);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetTimingAttribute(
+ IntPtr taskHandle,
+ int attribute,
+ out object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxSetTimingAttribute(
+ IntPtr taskHandle,
+ int attribute,
+ object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxResetTimingAttribute(IntPtr taskHandle, int attribute);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxGetTimingAttributeEx(
+ IntPtr taskHandle,
+ string deviceNames,
+ int attribute,
+ out object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxSetTimingAttributeEx(
+ IntPtr taskHandle,
+ string deviceNames,
+ int attribute,
+ object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ internal static extern int DAQmxResetTimingAttributeEx(
+ IntPtr taskHandle,
+ string deviceNames,
+ int attribute);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxDisableStartTrig(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgDigEdgeStartTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerEdge);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgEdgeStartTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerSlope,
+ double triggerLevel);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgMultiEdgeStartTrig(
+ IntPtr taskHandle,
+ string triggerSources,
+ int[] triggerSlopeArray,
+ double[] triggerLevelArray,
+ uint arraySize);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgWindowStartTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerWhen,
+ double windowTop,
+ double windowBottom);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgTimeStartTrig(
+ IntPtr taskHandle,
+ CVIAbsoluteTime when,
+ int timescale);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgDigPatternStartTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ string triggerPattern,
+ int triggerWhen);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxDisableRefTrig(IntPtr taskHandle);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgDigEdgeRefTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerEdge,
+ uint pretriggerSamples);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgEdgeRefTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerSlope,
+ double triggerLevel,
+ uint pretriggerSamples);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgMultiEdgeRefTrig(
+ IntPtr taskHandle,
+ string triggerSources,
+ int[] triggerSlopeArray,
+ double[] triggerLevelArray,
+ uint pretriggerSamples,
+ uint arraySize);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgAnlgWindowRefTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ int triggerWhen,
+ double windowTop,
+ double windowBottom,
+ uint pretriggerSamples);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxCfgDigPatternRefTrig(
+ IntPtr taskHandle,
+ string triggerSource,
+ string triggerPattern,
+ int triggerWhen,
+ uint pretriggerSamples);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxGetTrigAttribute(
+ IntPtr taskHandle,
+ int attribute,
+ out object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxSetTrigAttribute(IntPtr taskHandle, int attribute, object value);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxResetTrigAttribute(IntPtr taskHandle, int attribute);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxWriteAnalogF64(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ bool autoStart,
+ double timeout,
+ bool dataLayout,
+ double[] writeArray,
+ out IntPtr sampsPerChanWritten,
+ IntPtr reserved);
+
+ [DllImport("DAQmx", CallingConvention = CallingConvention.StdCall)]
+ public static extern int DAQmxWriteDigitalLines(
+ IntPtr taskHandle,
+ int numSampsPerChan,
+ bool autoStart,
+ double timeout,
+ bool dataLayout,
+ byte[] writeArray,
+ out IntPtr sampsPerChanWritten,
+ IntPtr reserved);
+
+}
\ No newline at end of file
diff --git a/Helpers/DeterministicHashCodeHelper.cs b/Helpers/DeterministicHashCodeHelper.cs
index 376ee82..4b99fd2 100644
--- a/Helpers/DeterministicHashCodeHelper.cs
+++ b/Helpers/DeterministicHashCodeHelper.cs
@@ -1,6 +1,12 @@
using CliWrap;
using File_Watcher.Models;
+
+#if ShellProgressBar
+
using ShellProgressBar;
+
+#endif
+
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Imaging;
@@ -21,13 +27,24 @@ internal static partial class DeterministicHashCodeHelper
{
public long Ticks { get; init; }
- public int? CurrentTick => _ProgressBar?.CurrentTick;
+ public int? CurrentTick =>
+#if ShellProgressBar
+ _ProgressBar?.CurrentTick;
+#else
+ throw new NotSupportedException("ShellProgressBar is not supported in this context.");
+#endif
+#if ShellProgressBar
private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions;
+#endif
public Windows() =>
+#if ShellProgressBar
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
+#else
+ throw new NotSupportedException("ShellProgressBar is not supported in this context.");
+#endif
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
GetDeterministicHashCode(httpClient, uri);
@@ -98,18 +115,26 @@ internal static partial class DeterministicHashCodeHelper
}
void IWindows.Tick() =>
+#if ShellProgressBar
_ProgressBar?.Tick();
+#else
+ throw new NotSupportedException("ShellProgressBar is not supported in this context.");
+#endif
void IDisposable.Dispose()
{
+#if ShellProgressBar
_ProgressBar?.Dispose();
+#endif
GC.SuppressFinalize(this);
}
void IWindows.ConstructProgressBar(int maxTicks, string message)
{
+#if ShellProgressBar
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
+#endif
}
ReadOnlyCollection IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath)
diff --git a/Helpers/NationalInstrumentsHelper.cs b/Helpers/NationalInstrumentsHelper.cs
new file mode 100644
index 0000000..9a586b3
--- /dev/null
+++ b/Helpers/NationalInstrumentsHelper.cs
@@ -0,0 +1,75 @@
+using File_Watcher.Models;
+using Helpers.DAQmx;
+
+namespace File_Watcher.Helpers;
+
+internal static partial class NationalInstrumentsHelper
+{
+
+ private static Dictionary? _DataAcquisitionTasks = null;
+
+ internal static bool WriteData(AppSettings appSettings, ILogger logger)
+ {
+ double value;
+ LogNetToHoursSince(logger);
+ if (_DataAcquisitionTasks is null)
+ {
+ string name;
+ _DataAcquisitionTasks = [];
+ DAQmxTask dataAcquisitionTask;
+ const DAQmxUnits volts = DAQmxUnits.Volts;
+ NationalInstrumentsConfiguration ni = appSettings.NationalInstrumentsConfiguration;
+ const DAQmxInputTerminalConfiguration differential = DAQmxInputTerminalConfiguration.Differential;
+ const DAQmxInputTerminalConfiguration referencedSingleEnded = DAQmxInputTerminalConfiguration.ReferencedSingleEnded;
+ foreach (string physicalChannel in appSettings.NationalInstrumentsConfiguration.DifferentialPhysicalChannels.Distinct())
+ {
+ name = !physicalChannel.Contains('/') ? physicalChannel : physicalChannel.Split('/')[1];
+ dataAcquisitionTask = DAQmxTask.Create(name);
+ dataAcquisitionTask.CreateAnalogInputVoltageChannel(physicalChannel, name, differential, ni.MiniumValue, ni.MaximumValue, volts, ni.CustomScaleName);
+ _DataAcquisitionTasks.Add(name, dataAcquisitionTask);
+ }
+ foreach (string physicalChannel in appSettings.NationalInstrumentsConfiguration.ReferencedSingleEndedPhysicalChannels.Distinct())
+ {
+ name = !physicalChannel.Contains('/') ? physicalChannel : physicalChannel.Split('/')[1];
+ dataAcquisitionTask = DAQmxTask.Create(name);
+ dataAcquisitionTask.CreateAnalogInputVoltageChannel(physicalChannel, name, referencedSingleEnded, ni.MiniumValue, ni.MaximumValue, volts, ni.CustomScaleName);
+ _DataAcquisitionTasks.Add(name, dataAcquisitionTask);
+ }
+ }
+ foreach (KeyValuePair keyValuePair in _DataAcquisitionTasks)
+ {
+ if (appSettings.NationalInstrumentsConfiguration.UsePointerMethod)
+ throw new NotSupportedException("Pointer method is not supported in this implementation.");
+ value = keyValuePair.Value.ReadAnalogScalarF64(appSettings.NationalInstrumentsConfiguration.ReadTimeout);
+ logger.LogInformation("{key}-{read}: {value}", keyValuePair.Key, keyValuePair.Value.TotalSamplesRead, value);
+ }
+ return true;
+ }
+
+ private static void LogNetToHoursSince(ILogger? logger)
+ {
+ double secondsInAHour = 3600f;
+ long epoch = new DateTime(1970, 1, 1).Ticks;
+ long net8ReleaseDate = new DateTime(2023, 11, 14).Ticks;
+ long net9ReleaseDate = new DateTime(2024, 11, 12).Ticks;
+ long net10ReleaseDate = new DateTime(2026, 01, 01).Ticks;
+ long framework48ReleaseDate = new DateTime(2019, 04, 18).Ticks;
+ double net8TotalSeconds = new TimeSpan(net8ReleaseDate - epoch).TotalSeconds;
+ double net9TotalSeconds = new TimeSpan(net9ReleaseDate - epoch).TotalSeconds;
+ double net10TotalSeconds = new TimeSpan(net10ReleaseDate - epoch).TotalSeconds;
+ double framework48TotalSeconds = new TimeSpan(framework48ReleaseDate - epoch).TotalSeconds;
+ logger?.LogInformation("It has been {net8TotalSeconds} seconds since net8 was released", net8TotalSeconds);
+ logger?.LogInformation("It has been {net9TotalSeconds} seconds since net9 was released", net9TotalSeconds);
+ logger?.LogInformation("It has been {net10TotalSeconds} seconds since net10 was released", net10TotalSeconds);
+ logger?.LogInformation("It has been {framework48TotalSeconds} seconds since framework48 was released", framework48TotalSeconds);
+ double net8TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net8TotalSeconds) / secondsInAHour);
+ double net9TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net9TotalSeconds) / secondsInAHour);
+ double net10TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net10TotalSeconds) / secondsInAHour);
+ double framework48TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - framework48TotalSeconds) / secondsInAHour);
+ logger?.LogInformation("It has been {net8TotalHours} hours since net8 was released", net8TotalHours);
+ logger?.LogInformation("It has been {net9TotalHours} hours since net9 was released", net9TotalHours);
+ logger?.LogInformation("It has been {net10TotalHours} hours since net10 was released", net10TotalHours);
+ logger?.LogInformation("It has been {framework48TotalHours} hours since framework48 was released", framework48TotalHours);
+ }
+
+}
\ No newline at end of file
diff --git a/Models/AppSettings.cs b/Models/AppSettings.cs
index b8bb17a..4d74f36 100644
--- a/Models/AppSettings.cs
+++ b/Models/AppSettings.cs
@@ -16,6 +16,7 @@ public record AppSettings(CamstarOracleConfiguration CamstarOracleConfiguration,
IsoConfiguration IsoConfiguration,
MetadataSettings MetadataSettings,
MetrologyConfiguration MetrologyConfiguration,
+ NationalInstrumentsConfiguration NationalInstrumentsConfiguration,
NugetConfiguration NugetConfiguration,
ResultSettings ResultSettings,
SeleniumConfiguration SeleniumConfiguration,
@@ -42,6 +43,7 @@ public record AppSettings(CamstarOracleConfiguration CamstarOracleConfiguration,
IsoConfiguration? isoConfiguration = configurationRoot.GetSection(nameof(IsoConfiguration)).Get();
MetadataSettings? metadataSettings = configurationRoot.GetSection(nameof(MetadataSettings)).Get();
MetrologyConfiguration? metrologyConfiguration = configurationRoot.GetSection(nameof(MetrologyConfiguration)).Get();
+ NationalInstrumentsConfiguration? nationalInstrumentsConfiguration = configurationRoot.GetSection(nameof(NationalInstrumentsConfiguration)).Get();
NugetConfiguration? nugetConfiguration = configurationRoot.GetSection(nameof(NugetConfiguration)).Get();
ResultSettings? resultSettings = configurationRoot.GetSection(nameof(ResultSettings)).Get();
SeleniumConfiguration? seleniumConfiguration = configurationRoot.GetSection(nameof(SeleniumConfiguration)).Get();
@@ -63,6 +65,7 @@ public record AppSettings(CamstarOracleConfiguration CamstarOracleConfiguration,
|| isoConfiguration is null
|| metadataSettings is null
|| metrologyConfiguration is null
+ || nationalInstrumentsConfiguration is null
|| nugetConfiguration is null
|| resultSettings is null
|| seleniumConfiguration is null
@@ -96,6 +99,7 @@ public record AppSettings(CamstarOracleConfiguration CamstarOracleConfiguration,
isoConfiguration,
metadataSettings,
metrologyConfiguration,
+ nationalInstrumentsConfiguration,
nugetConfiguration,
resultSettings,
seleniumConfiguration,
diff --git a/Models/NationalInstrumentsConfiguration.cs b/Models/NationalInstrumentsConfiguration.cs
new file mode 100644
index 0000000..67e2b82
--- /dev/null
+++ b/Models/NationalInstrumentsConfiguration.cs
@@ -0,0 +1,27 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace File_Watcher.Models;
+
+public record NationalInstrumentsConfiguration(string CustomScaleName,
+ string[] DifferentialPhysicalChannels,
+ int MaximumValue,
+ int MiniumValue,
+ string[] ReferencedSingleEndedPhysicalChannels,
+ int ReadTimeout,
+ bool UsePointerMethod)
+{
+
+ public override string ToString()
+ {
+ string result = JsonSerializer.Serialize(this, NationalInstrumentsConfigurationSourceGenerationContext.Default.NationalInstrumentsConfiguration);
+ return result;
+ }
+
+}
+
+[JsonSourceGenerationOptions(WriteIndented = true)]
+[JsonSerializable(typeof(NationalInstrumentsConfiguration))]
+internal partial class NationalInstrumentsConfigurationSourceGenerationContext : JsonSerializerContext
+{
+}
\ No newline at end of file
diff --git a/NancyModules/SyncModule.cs b/NancyModules/SyncModule.cs
index 6ecffa3..c1830da 100644
--- a/NancyModules/SyncModule.cs
+++ b/NancyModules/SyncModule.cs
@@ -1,3 +1,5 @@
+#if Nancy
+
using Nancy;
using Nancy.Extensions;
@@ -48,4 +50,6 @@ public class SyncModule : NancyModule
});
}
-}
\ No newline at end of file
+}
+
+#endif
\ No newline at end of file
diff --git a/Startup.cs b/Startup.cs
index 0d81aff..eb09ac9 100644
--- a/Startup.cs
+++ b/Startup.cs
@@ -1,3 +1,5 @@
+#if Nancy
+
using Microsoft.Owin.Cors;
using Nancy.Owin;
using Owin;
@@ -16,4 +18,6 @@ public class Startup
_ = app.UseNancy();
}
-}
\ No newline at end of file
+}
+
+#endif
\ No newline at end of file
diff --git a/Worker.cs b/Worker.cs
index 0c2e1b2..dd9ce5e 100644
--- a/Worker.cs
+++ b/Worker.cs
@@ -24,7 +24,9 @@ public partial class Worker : BackgroundService
_IsWindowsService = collection.Contains(nameof(WindowsServiceLifetime));
if (appSettings.FileWatcherConfiguration.Helper == nameof(Helpers.SyncHelper))
{
+#if Nancy
_ = Microsoft.Owin.Hosting.WebApp.Start(appSettings.SyncConfiguration.UniformResourceLocator);
+#endif
logger.LogInformation("Server running on {url}", appSettings.SyncConfiguration.UniformResourceLocator);
}
}
@@ -87,6 +89,7 @@ public partial class Worker : BackgroundService
nameof(Helpers.HelperWaferCounter) => Helpers.HelperWaferCounter.MoveFile(_AppSettings, _Logger),
nameof(Helpers.HelperSerial) => Helpers.HelperSerial.ReadWrite(_AppSettings, _Logger, cancellationToken),
nameof(Helpers.HelperMetrologyFiles) => Helpers.HelperMetrologyFiles.SortAndDelete(_AppSettings, _Logger),
+ nameof(Helpers.NationalInstrumentsHelper) => Helpers.NationalInstrumentsHelper.WriteData(_AppSettings, _Logger),
nameof(Helpers.DeterministicHashCodeHelper) => Helpers.DeterministicHashCodeHelper.WindowsWork(_AppSettings, _Logger),
#if Selenium
nameof(Helpers.SeleniumHelper) => Helpers.SeleniumHelper.HyperTextMarkupLanguageToPortableNetworkGraphics(_AppSettings, _Logger),