//============================================================================================== /// <summary> /// Synchronizes access to the bulk in ready buffers queue /// </summary> /// <param name="bulkInBuffer">The buffer to queue</param> /// <param name="queueAction">The actions to take (enqueue or dequeue)</param> /// <returns>The buffer that was dequeued</returns> //============================================================================================== internal BulkInBuffer QueueBulkInReadyBuffers(BulkInBuffer bulkInBuffer, QueueAction queueAction) { lock (m_bulkInReadyBuffersLock) { if (queueAction == QueueAction.Enqueue) { m_bulkInReadyBuffers.Enqueue(bulkInBuffer); return null; } else { if (m_bulkInReadyBuffers.Count > 0) { return m_bulkInReadyBuffers.Dequeue(); } else { return null; } } } }
//============================================================================================== /// <summary> /// Virtual method for a USB Bulk IN request /// </summary> /// <param name="buffer">The buffer to receive the data</param> /// <param name="bytesReceived">The number of actual bytes received</param> /// <returns>The result</returns> //============================================================================================== internal abstract ErrorCodes UsbBulkInRequest(ref BulkInBuffer buffer, ref uint bytesReceived);
//============================================================================================== /// <summary> /// Synchronizes access to the bulk in completed buffers queue /// </summary> /// <param name="bulkInBuffer">The buffer to queue</param> /// <param name="queueAction">The actions to take (enqueue or dequeue)</param> /// <returns>The buffer that was dequeued</returns> //============================================================================================== internal BulkInBuffer QueueBulkInCompletedBuffers(BulkInBuffer bulkInBuffer, QueueAction queueAction) { lock (m_bulkInCompletedBuffersLock) { try { if (queueAction == QueueAction.Enqueue) { m_bulkInCompletedBuffers.Enqueue(bulkInBuffer); return null; } else { if (m_bulkInCompletedBuffers.Count > 0) { return m_bulkInCompletedBuffers.Dequeue(); } else { return null; } } } catch (Exception ex) { System.Diagnostics.Debug.Assert(false, ex.Message); return null; } } }
//============================================================================================== /// <summary> /// Virtual method for a USB Bulk IN request /// </summary> /// <param name="buffer">The buffer to receive the data</param> /// <param name="bytesRequested">The number of bytes to requested</param> /// <param name="bytesReceived">The number of actual bytes received</param> /// <returns>The result</returns> //============================================================================================== internal override ErrorCodes UsbBulkInRequest(ref BulkInBuffer buffer, ref uint bytesReceived) { ErrorCodes result; BulkInBuffer bulkInBuffer = null; do { result = m_inputScanErrorCode; if (result != ErrorCodes.NoErrors) break; bulkInBuffer = QueueBulkInCompletedBuffers(null, QueueAction.Dequeue); if (bulkInBuffer == null) Thread.Sleep(1); } while (bulkInBuffer == null && !m_stopInputTransfers); if (bulkInBuffer != null) { buffer = bulkInBuffer; bytesReceived = (uint)bulkInBuffer.Length; } else { buffer = null; bytesReceived = 0; } return result; }
//============================================================================================================= /// <summary> /// Sets up parameters for bulk in transfers /// </summary> /// <param name="scanRate">The device scan rate</param> /// <param name="totalNumberOfBytes">The total number of bytes to transfer</param> /// <param name="transferSize">The number of bytes in each transfer request</param> //============================================================================================================= internal override void PrepareInputTransfers(double scanRate, int totalNumberOfBytes, int transferSize) { m_errorCode = ErrorCodes.NoErrors; m_expectedInputTransferIndex = 0; m_totalNumberOfInputBytesRequested = totalNumberOfBytes; m_completedBulkInRequestBuffers.Clear(); //For the 1208FS/1408FS, packets get mixed up at high rates in SINGLEIO mode. So, // only allow 1 packet to be in process int maxWorkingInputRequests = (transferSize==2 ? 2 : 8); if (m_criticalParams.InputSampleMode == SampleMode.Finite) { m_numberOfWorkingInputRequests = Math.Min(maxWorkingInputRequests, Math.Max(1, totalNumberOfBytes / transferSize)); m_numberOfQueuedInputRequests = Math.Max(1, m_numberOfWorkingInputRequests / 2); } else { m_numberOfWorkingInputRequests = maxWorkingInputRequests; m_numberOfQueuedInputRequests = Math.Max(1,maxWorkingInputRequests/2); } if (m_criticalParams.InputTransferMode == TransferMode.SingleIO) { int byteRatio = m_criticalParams.DataInXferSize; // for single io mode at higher rates the packets could get out of order between the kernel driver // and user mode so only use one request buffer... m_numberOfWorkingInputRequests = 1; m_numberOfQueuedInputRequests = 1; m_totalNumberOfInputRequests = totalNumberOfBytes / (byteRatio * m_criticalParams.NumberOfSamplesForSingleIO); } else { if (totalNumberOfBytes <= transferSize) m_totalNumberOfInputRequests = 1; else m_totalNumberOfInputRequests = (int)Math.Ceiling((double)totalNumberOfBytes / (double)transferSize); } // the device will send a zero-length packet after the last data packet if // the number of bytes is a multiple of the packet size so add an extra request (or is it the transfer size) //if ((totalNumberOfBytes % m_criticalParams.InputPacketSize == 0) || // (m_criticalParams.InputTransferMode == TransferMode.SingleIO && m_criticalParams.Requires0LengthPacketForSingleIO)) //{ // m_totalNumberOfInputRequests++; // if (m_numberOfWorkingInputRequests == 1)// && m_criticalParams.NumberOfSamplesForSingleIO > 1) // m_numberOfWorkingInputRequests++; //} m_stopInputTransfers = false; int numberOfBulkInCopyBuffers = 50; // empty the bulkInReadyBuffers queue while (m_bulkInReadyBuffers.Count > 0) { BulkInBuffer buffer = m_bulkInReadyBuffers.Dequeue(); new WeakReference(buffer, false); buffer = null; } // empty the bulkInCompletedBuffers queue while (m_bulkInCompletedBuffers.Count > 0) { BulkInBuffer buffer = m_bulkInCompletedBuffers.Dequeue(); new WeakReference(buffer, false); buffer = null; } //GC.Collect(); for (int i = 0; i < numberOfBulkInCopyBuffers; i++) { QueueBulkInReadyBuffers(new BulkInBuffer(transferSize), QueueAction.Enqueue); } // create the holding queue base on the number of working requests... m_holdingBuffer = new BulkInBuffer[m_numberOfWorkingInputRequests]; for (int i = 0; i < m_numberOfWorkingInputRequests; i++) { // initialize to null... m_holdingBuffer[i] = null; } m_temporaryBuffer = new List<BulkInBuffer>(); // create the bulk in request objects that will be used in the transfers... CreateBulkInputRequestObjects(transferSize); // reset the sumbitted and completed counts... m_numberOfInputRequestsSubmitted = 0; m_numberOfInputRequestsCompleted = 0; // queue bulk in requests - at this point the device has not yet started.. QueueBulkInRequests(scanRate); }
//============================================================================================================= /// <summary> /// Virtual method for a USB Bulk IN request /// </summary> /// <param name="buffer">The buffer to receive the data</param> /// <param name="bytesRequested">The number of bytes to requested</param> /// <param name="bytesReceived">The number of actual bytes received</param> /// <returns>The result</returns> //============================================================================================================= internal override ErrorCodes UsbBulkInRequest(ref BulkInBuffer buffer, ref uint bytesReceived) { System.Diagnostics.Debug.Assert(false, "UsbBulkInRequest must be implemented in a derived class"); buffer = null; bytesReceived = 0; return ErrorCodes.MethodRequiresImplementation; }
//==================================================================== /// <summary> /// Processes bulk read requests for input scan on a separate thread /// This method will copy bulk in request buffers to an internal /// managed buffer or an extenral unmanaged buffer /// </summary> //==================================================================== protected void ProcessInputScanThread() { System.Diagnostics.Debug.Assert(m_criticalParams.BulkInXferSize != 0); bool usingInternalBuffer = true; //if (m_onAcquisitionArmedCallbackControl != null) // m_onAcquisitionArmedCallback = new CallbackDelegate(m_onAcquisitionArmedCallbackControl.NotifyApplication); if (m_onDataAvailableCallbackControl != null) m_onDataAvailableCallback = new CallbackDelegate(m_onDataAvailableCallbackControl.NotifyApplication); if (m_onInputScanCompleteCallbackControl != null) m_onInputScanCompleteCallback = new CallbackDelegate(m_onInputScanCompleteCallbackControl.NotifyApplication); if (m_onInputScanErrorCallbackControl != null) m_onInputScanErrorCallback = new CallbackDelegate(m_onInputScanErrorCallbackControl.NotifyApplication); int triggerRearmByteCount = 0; int readBufferLength; int callbackCount = 0; int rearmTriggerCount = 0; m_availableSamplesForCallbackSinceStartOfScan = 0; m_terminateCallbacks = false; m_totalBytesReceived = 0; if (m_onDataAvailableCallbackControl != null || m_onInputScanErrorCallbackControl != null || m_onInputScanCompleteCallbackControl != null) { TerminateCallbacks = false; // the other callback controls may be instantiated but this may not be... if (m_onDataAvailableCallbackControl != null) { m_onDataAvailableCallbackControl.Abort = false; } // start the callback thread... m_callbackThread = new Thread(new ThreadStart(ProcessCallbackThread)); m_callbackThread.Name = "[DAQFlex]: InputScanCallbackThread"; m_callbackThread.Start(); } unsafe { if (m_externalReadBuffer != null) usingInternalBuffer = false; } // get the buffer size that the platform interop object calculates from the scan rate int optimalBufferSize = m_criticalParams.BulkInXferSize; int channelCount = m_criticalParams.AiChannelCount; m_totalBytesToRead = channelCount * (m_criticalParams.DataInXferSize * m_criticalParams.InputScanSamples); if (m_criticalParams.InputSampleMode == SampleMode.Continuous && !m_inputBufferSizeOverride) m_internalReadBuffer = new byte[2 * m_totalBytesToRead]; if (usingInternalBuffer) readBufferLength = m_internalReadBuffer.Length; else readBufferLength = m_externalReadBufferSize; if (m_totalBytesToRead < optimalBufferSize) { optimalBufferSize = m_totalBytesToRead; m_criticalParams.BulkInXferSize = optimalBufferSize; } // give the device an opportunity to do device-specific stuff before starting m_daqDevice.BeginInputScan(); if (m_criticalParams.InputTransferMode == TransferMode.SingleIO) { // single sample only optimalBufferSize = m_criticalParams.DataInXferSize * m_criticalParams.NumberOfSamplesForSingleIO; m_criticalParams.BulkInXferSize = optimalBufferSize; } if (m_onDataAvailableCallbackControl != null) { // update the bulk transfer size so it closely matches the number of call back samples... optimalBufferSize = GetOptimalInputBufferSize(m_criticalParams.InputScanRate); } // the platform interop object will allocate and return the bulk read buffer m_bulkReadBuffer = null; //// give the device an opportunity to do device-specific stuff before starting //m_daqDevice.BeginInputScan(); // this will queue one or more bulk in requests if the interop object supports asynchronous I/O m_platformInterop.PrepareInputTransfers(m_criticalParams.InputScanRate, m_totalBytesToRead, optimalBufferSize); //// were ready to send the start command so raise the acqusition armed event //if (m_onAcquisitionArmedCallbackControl != null && m_onAcquisitionArmedCallbackControl.Created) //{ // AcquisitionInfo acquisitionInfo = new AcquisitionInfo(); // acquisitionInfo.AiQueueEnabled = m_criticalParams.AiQueueEnabled; // acquisitionInfo.AiScans = (optimalBufferSize / channelCount / sizeof(double)); // acquisitionInfo.InputScanRate = m_criticalParams.InputScanRate; // acquisitionInfo.IsAiDataCalibrated = m_criticalParams.CalibrateAiData; // if (m_onAcquisitionArmedCallbackControl.ExecuteOnUIThread) // { // m_acquisitionArmedCallbackParam[0] = acquisitionInfo; // m_onAcquisitionArmedCallbackControl.BeginInvoke(m_onAcquisitionArmedCallback, m_acquisitionArmedCallbackParam); // } // else // { // m_acquisitionArmedCallbackParam[0] = acquisitionInfo; // m_onAcquisitionArmedCallbackControl.NotifyApplication((int)m_acquisitionArmedCallbackParam[0]); // } //} // this will start the device scan TransmitDeferredInputMessages(); int numberOfBulkTransfersToExecute; uint bytesReceivedInCurrentTransfer = 0; uint totalBytesTransfered = 0; uint bytesToTransfer = 0; int deltaBytes = 0; while (ContinueProcessingInputScan(m_inputScanErrorCode)) { bytesReceivedInCurrentTransfer = 0; totalBytesTransfered = 0; bytesToTransfer = 0; m_inputScanStatus = ScanState.Running; // if the bulk read buffer length is greater than the max transfer size, then we'll need multiple transfers numberOfBulkTransfersToExecute = (int)Math.Ceiling((double)optimalBufferSize / (double)m_platformInterop.MaxTransferSize); for (int i = 0; i < numberOfBulkTransfersToExecute; i++) { // calculate the number of bytes to process in this transfer if (m_criticalParams.InputTransferMode == TransferMode.SingleIO) bytesToTransfer = (uint)(m_criticalParams.DataInXferSize * m_criticalParams.NumberOfSamplesForSingleIO); else bytesToTransfer = (uint)Math.Min(optimalBufferSize, (m_totalBytesToRead - (int)totalBytesTransfered)); // if the input scan is finite then check if this is the last transfer if (m_criticalParams.InputSampleMode == SampleMode.Finite) { if (m_lastInputScanWriteIndex + bytesToTransfer > readBufferLength) bytesToTransfer = (uint)(readBufferLength - m_lastInputScanWriteIndex) - 1; } //********************************************************************************************************* // Read the data on the bulk in pipe //********************************************************************************************************* m_inputScanErrorCode = m_platformInterop.UsbBulkInRequest(ref m_bulkReadBuffer, ref bytesReceivedInCurrentTransfer); // update the total number of bytes received m_totalBytesReceived += (ulong)bytesReceivedInCurrentTransfer; // m_bulkInReadBuffer could be null if the input scan was stopped with the Stop command if (m_inputScanErrorCode == (int)ErrorCodes.NoErrors && m_bulkReadBuffer != null) { // update the total number of bytes transfered so far totalBytesTransfered += bytesReceivedInCurrentTransfer; try { if (m_criticalParams.DeltaRearmInputSamples > 0) { triggerRearmByteCount += (int)bytesToTransfer; if (triggerRearmByteCount >= (m_criticalParams.DataInXferSize * channelCount * m_criticalParams.AdjustedRearmSamplesPerTrigger)) { rearmTriggerCount++; bytesToTransfer -= (uint)(m_criticalParams.DataInXferSize * channelCount * m_criticalParams.DeltaRearmInputSamples); m_totalBytesReceived -= (ulong)(m_criticalParams.DataInXferSize * channelCount * m_criticalParams.DeltaRearmInputSamples); triggerRearmByteCount = 0; } } // upate the number of samples acquired so far per channel lock (m_inputScanCountLock) { m_inputScanCount = (m_totalBytesReceived / (ulong)m_criticalParams.DataInXferSize) / (ulong)channelCount; if (m_criticalParams.InputScanOverwrite) { deltaBytes = m_criticalParams.DataInXferSize * (int)(m_inputScanCount - m_inputSamplesReadPerChannel); if (deltaBytes > readBufferLength) { m_inputScanErrorCode = ErrorCodes.InputBufferOverrun; continue; } } } if (m_criticalParams.InputTransferMode == TransferMode.SingleIO) callbackCount += ((int)bytesReceivedInCurrentTransfer / m_criticalParams.DataInXferSize) / m_criticalParams.NumberOfSamplesForSingleIO; else callbackCount += ((int)bytesReceivedInCurrentTransfer / m_criticalParams.DataInXferSize) / channelCount; if (usingInternalBuffer) { int bytesToCopyOnFirstPass; int bytesToCopyOnSecondPass; if (m_lastInputScanWriteIndex + bytesToTransfer >= readBufferLength) { // two passes are required since the current input scan write index // wrapped around to the beginning of the internal read buffer bytesToCopyOnFirstPass = readBufferLength - (m_lastInputScanWriteIndex + 1); bytesToCopyOnSecondPass = (int)bytesToTransfer - bytesToCopyOnFirstPass; m_inputBufferFilled = true; } else { // only one pass is required since the current input scan write index // did not wrap around bytesToCopyOnFirstPass = (int)bytesToTransfer; bytesToCopyOnSecondPass = 0; } CopyToInternalReadBuffer(m_bulkReadBuffer.Data, m_internalReadBuffer, bytesToCopyOnFirstPass, bytesToCopyOnSecondPass); } else { unsafe { CopyToExternalReadBuffer(m_bulkReadBuffer.Data, m_externalReadBuffer, readBufferLength, bytesToTransfer); } } // calculate current index (some devices in SINGLEIO mode only transfer one channel per packet and the second calculation will always return 0) if (m_criticalParams.InputTransferMode == TransferMode.SingleIO) m_inputScanIndex = (long)Math.Max(0, (m_inputScanCount % (ulong)(readBufferLength / m_criticalParams.DataInXferSize)) - 1); else m_inputScanIndex = (long)Math.Max(0, (m_inputScanCount % (ulong)(readBufferLength / m_criticalParams.DataInXferSize)) - (ulong)channelCount); // add the m_bulkReadBuffer to the ready buffers queue so it may be reused m_platformInterop.QueueBulkInReadyBuffers(m_bulkReadBuffer, QueueAction.Enqueue); } catch (Exception ex) { System.Diagnostics.Debug.Assert(false, ex.Message); m_inputScanErrorCode = ErrorCodes.UnknownError; } //***************************************************************************************** // OnDataAvailable callback //***************************************************************************************** if (m_inputScanErrorCode == ErrorCodes.NoErrors) { if (m_onDataAvailableCallbackControl != null && m_onDataAvailableCallbackControl.Created) { if (m_criticalParams.InputSampleMode == SampleMode.Finite) { if (m_totalBytesReceived >= (ulong)m_totalBytesToRead && m_totalBytesReceived > 0) TerminateCallbacks = true; } } } } } //***************************************************************************************** // OnInputScanError callback //***************************************************************************************** if (m_inputScanErrorCode != ErrorCodes.NoErrors) { m_callbackInfoQueue.Clear(); TerminateCallbacks = true; } Thread.Sleep(0); } while (!m_platformInterop.InputScanComplete()) Thread.Sleep(0); DebugLogger.WriteLine("Input scan complete"); DebugLogger.StopWatch.Stop(); //DebugLogger.DumpDebugInfo(); if (m_inputScanErrorCode == ErrorCodes.DataOverrun) m_inputScanStatus = ScanState.Overrun; else m_inputScanStatus = ScanState.Idle; m_inputScanComplete = true; m_inputScanStarted = false; // give the device an opportunity to do device-specific stuff before stopping // THIS MUST BE DONE AFTER m_inputScanStatus HAS BEEN SET m_daqDevice.EndInputScan(); if (m_callbackThread != null) { // was a stop message sent?... if (m_stopInputScan) { // Does the callback thread know to terminate?... if (!TerminateCallbacks) { // Let it know... TerminateCallbacks = true; } } } m_errorCode = m_inputScanErrorCode; // set the DaqDevice's pending error so that it can handle it on the next message sent... if (m_errorCode != ErrorCodes.NoErrors && m_errorCode != ErrorCodes.DataOverrun) { m_daqDevice.SetPendingInputScanError(m_errorCode); } }