/// <summary> /// Default Constructor for DataSource. /// If autoOutput, then this object will output one Sample every secondsPerStep /// to its Followers; if not autoOutput, then it will only generate output when /// requested by a call to trigger() or getNextValue() or getNextValues(int). /// </summary> /// <param name="secondsPerStep">Interval between samples, in seconds or fraction thereof, /// e.g. 1 KHz = 1 ms per sample would have secondsPerStep = 0.001</param> /// <param name="autoOutput">If true, then we push data out to the Followers; /// if false, we wait for whoever to pull out the data</param> public DataSource(double secondsPerStep, bool autoOutput) { // Set the interval: stepSize = secondsPerStep; // Set whether we output on a push or pull scheme: autoSend = autoOutput; if (autoSend) { // Init the TimerThread: sendTimer = new TimerThread( new System.Threading.ThreadStart(this.next), true); // true -> wait for one call to complete before starting the next one // false ); // false -> do NOT wait for one call to complete before starting the next one // (this can cause overlaps and long queues of waiting threads if the // code execution should halt for any reason, such as a msg. box or // a debugger breakpoint). } // Make sure we init. this, otherwise we risk BIG problems! // Note: each subclass should re-init lastOutput to what makes sense // for that class. lastOutput = DateTime.Now; this.lastStep = 0; this.lastOutput = DateTime.Now; }
/// <summary> /// Override default behavior to: /// - Save a local copy /// - Get file header info /// - Update iData accordingly /// - Allow using header info to drive the output rates /// </summary> public override void init(ChainInfo iData) { // Save a local handle: // - If we're AutoSending, we're the authority on the data, so we can override the // contents of iData. That means we point to iData and modify that. // - If we're not AutoSending, we can tell anyone downstream what the actual data // params are, but we shouldn't mess with the original iData to avoid affecting // the source. if (autoSend) { // Point to the input data structure: initValues = iData; } else { // Make our own data structure: initValues = new ChainInfo(); initValues.samplingPeriodSec = iData.samplingPeriodSec; } if (autoSend && doneReading) // We terminated the timer earlier - restart it: { // Init the TimerThread: sendTimer = new TimerThread( new System.Threading.ThreadStart(this.next), false); } // Init. what needs it: curReadStartPos = 0; doneReading = false; dataFinished = false; // in case it was set to true previously if (initValues.patientInfo == null) { initValues.patientInfo = new PatientInfo("", "", ""); } if (initValues.dataInfo == null) { initValues.dataInfo = new DataInfo( ); } if (initValues.fileInfo == null) { initValues.fileInfo = new FileHandler( ); } // Read the header from the input file: readHeader(); // Init. the sampling period: this.stepSize = initValues.samplingPeriodSec; // Init output buffer: initBuffer(); // Apply values and propagate downstream: base.init(iData); // iData may have been changed or not }
/// <summary> /// Override default behavior to: /// - Save a local copy /// - Get file header info /// - Update iData accordingly /// - Allow using header info to drive the output rates. /// <para>NOTE: This method call GC.Collect() to force garbage collection on the /// managed-code environment. If you are running unmanaged code, before calling /// this method you must ensure that your unmanaged storage allocations are properly /// protected or released.</para> /// </summary> public override void init(ChainInfo iData) { string errMsg = null; // Reset THOROUGHLY: GC.Collect(); GC.WaitForPendingFinalizers(); WfdbAccess.CloseWfdb(); GC.Collect(); GC.WaitForPendingFinalizers(); // Read record header: siArray = WfdbAccess.GetSigInfoArray(numSignals, ref errMsg); bool result = WfdbAccess.GetSignalInfo(recordName, siArray, numSignals, ref errMsg); if (!result) { //System.Windows.Forms.MessageBox("Unable to access header for record '" + recordName + "' in path '" + searchPath + "'!"); Exception ex = new ArgumentException("Unable to access header for record '" + recordName + "' in path '" + searchPath + "'."); throw ex; } // Read useful info from header: WFDB_Siginfo sigInfo = siArray.getitem(0); buffSize = sigInfo.spf; frameBuffer = new int[buffSize]; numSamples = sigInfo.nsamp * sigInfo.spf; sigName = sigInfo.desc; //sampFreq = WfdbAccess.GetSamplingFrequency(recordName); //if (sampFreq < 1) //{ // Problem reading header! // //System.Windows.Forms.MessageBox("Unable to access record '" + recordName + "' in path '" + searchPath + "'!"); // Exception ex = new ArgumentException("Unable to access header for record '" + recordName + "' in path '" + searchPath + "'."); // throw ex; //} adcZeroRef = sigInfo.adczero; adcResBits = sigInfo.adcres; adcMaxValue = (int)Math.Pow(2, adcResBits); baseline = sigInfo.baseline; ampGain = sigInfo.gain; if (ampGain == 0) { // Use default: ampGain = 200; } dataUnits = sigInfo.units; if ((dataUnits == null) || (dataUnits.Length == 0)) { // Assign arbitrarily? dataUnits = "mV"; } // Save a local handle: if (autoSend) { // Point to the input data structure: initValues = iData; } else { // Make our own data structure: initValues = new ChainInfo(); initValues.samplingPeriodSec = iData.samplingPeriodSec; } if (autoSend && doneReading) // We previously terminated the timer - restart it: { // Init the TimerThread: sendTimer = new TimerThread( new System.Threading.ThreadStart(this.next), true); } // Init. what needs it: dispSamples = 0; // just started, nothing retrieved yet doneReading = false; if (initValues.patientInfo == null) { initValues.patientInfo = new PatientInfo("", "", ""); } if (initValues.dataInfo == null) { initValues.dataInfo = new DataInfo(sigInfo.desc, DateTime.Now.ToString()); } // And, apply the REAL values: initValues.dataInfo.BitsPerSample = adcResBits; initValues.dataInfo.ScaleMultiplier = ampGain.ToString(); if (adcResBits == 0) { // Missing some info -- go with a default: initValues.dataInfo.FullScaleReferenceVoltage = "2.0"; initValues.dataInfo.ZeroOffset = "0.0"; initValues.dataInfo.ZeroReferenceVoltage = "-2.0"; } else { initValues.dataInfo.FullScaleReferenceVoltage = ((adcMaxValue - baseline) / ampGain).ToString(); initValues.dataInfo.ZeroOffset = baseline.ToString(); initValues.dataInfo.ZeroReferenceVoltage = (-baseline / ampGain).ToString(); } initValues.dataInfo.ValueUnits = dataUnits; if (initValues.fileInfo == null) { initValues.fileInfo = new FileHandler(); } if (initValues.samplingPeriodSec != stepSize) { // Update the sampling period: initValues.samplingPeriodSec = stepSize; } //// Init. the sampling period: //this.stepSize = initValues.samplingPeriodSec; // Init output buffer: ReadBuffer(); // Apply values and propagate downstream: base.init(iData); // iData may have been changed or not }