public void ActivateDetector(Detector det = null) { if (!file) { DAQControl.ActivateDetector(det); } }
public void SetupEventHandlers() { LMLoggers.LognLM applog = NC.App.Loggers.Logger(LMLoggers.AppSection.App); /// set up the 7 magic event handlers /// here I have a base handler that does the same thing for every event (writes a string to the log) SetEventHandler(ActionEvents.EventType.PreAction, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.PreAction, applog, LogLevels.Verbose, o); }); SetEventHandler(ActionEvents.EventType.ActionPrep, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionPrep, applog, LogLevels.Verbose, o); }); SetEventHandler(ActionEvents.EventType.ActionStart, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionStart, applog, LogLevels.Verbose, o); }); SetEventHandler(ActionEvents.EventType.ActionInProgress, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionInProgress, applog, LogLevels.Verbose, o); }); SetEventHandler(ActionEvents.EventType.ActionStop, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionStop, applog, LogLevels.Warning, o); }); SetEventHandler(ActionEvents.EventType.ActionCancel, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionCancel, applog, LogLevels.Warning, o); }); SetEventHandler(ActionEvents.EventType.ActionFinished, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionFinished, applog, LogLevels.Info, o); NC.App.Opstate.SOH = NCC.OperatingState.Stopped; // in case we got here after a Cancel NC.App.Loggers.Flush(); }); NC.App.Opstate.SOH = NCC.OperatingState.Stopped; }
/// <summary> /// Performs an assay operation. /// </summary> /// <param name="measurement">The measurement.</param> /// <param name="cancellationToken">The cancellation token to observe.</param> /// <exception cref="Ptr32Exception">An error occurred communicating with the device.</exception> protected void PerformAssay(Measurement measurement, CancellationToken cancellationToken) { System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; try { m_logger.TraceEvent(LogLevels.Info, 0, "PTR-32[{0}]: Started assay", DeviceName); m_logger.Flush(); if (m_setvoltage) { SetVoltage(m_voltage, MaxSetVoltageTime, CancellationToken.None); } Stopwatch stopwatch = new Stopwatch(); TimeSpan duration = TimeSpan.FromSeconds(measurement.AcquireState.lm.Interval); byte[] buffer = new byte[1024 * 1024]; long total = 0; m_device.Reset(); stopwatch.Start(); m_logger.TraceEvent(LogLevels.Verbose, 11901, "{0} start", DateTime.Now.ToString()); while (stopwatch.Elapsed < duration) { cancellationToken.ThrowIfCancellationRequested(); if (m_device.Available > 0) { int bytesRead = m_device.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { RDT.PassBufferToTheCounters(buffer, 0, bytesRead); total += bytesRead; } } } stopwatch.Stop(); m_logger.TraceEvent(LogLevels.Verbose, 11901, "{0} stop", DateTime.Now.ToString()); lock (m_monitor) { m_cancellationTokenSource.Dispose(); m_cancellationTokenSource = null; } m_logger.TraceEvent(LogLevels.Info, 0, "PTR-32[{0}]: Finished assay; read {1} bytes in {2}s", DeviceName, total, stopwatch.Elapsed.TotalSeconds); m_logger.Flush(); DAQControl.HandleEndOfCycleProcessing(this, new Analysis.StreamStatusBlock(@"PTR32 Done")); } catch (OperationCanceledException) { m_logger.TraceEvent(LogLevels.Info, 0, "PTR-32[{0}]: Stopping assay", DeviceName); m_logger.Flush(); DAQControl.StopActiveAssayImmediately(); throw; } catch (Exception ex) { m_logger.TraceEvent(LogLevels.Error, 0, "PTR-32[{0}]: Error during assay: {1}", DeviceName, ex.Message); m_logger.TraceException(ex, true); m_logger.Flush(); DAQControl.HandleFatalGeneralError(this, ex); throw; } }
/// <summary> /// Performs a single cycle assay operation. /// </summary> /// <param name="measurement">The measurement parameters needed for the op.</param> /// <param name="cancellationToken">The cancellation token to observe.</param> /// <exception cref="MCADeviceLostConnectionException">An error occurred communicating with the device.</exception> /// <exception cref="MCADeviceBadDataException">An error occurred with the raw data stream state.</exception> /// <exception cref="Exception">Empty or corrupt runtime data structure.</exception> protected void PerformAssay(Measurement measurement, MeasTrackParams mparams, CancellationToken cancellationToken) { MCA527ProcessingState ps = (MCA527ProcessingState)(RDT.State); Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; try { if (ps == null) { throw new Exception("Big L bogus state"); // caught cleanly below } if (ps.writingFile && (ps.file == null || ps.file.writer == null)) { m_logger.TraceEvent(LogLevels.Verbose, 9, "null"); } ushort seq = mparams.seq; m_logger.TraceEvent(LogLevels.Info, 0, "MCA527[{0}]: Started assay {1}", DeviceName, seq); m_logger.Flush(); cancellationToken.ThrowIfCancellationRequested(); if (m_setvoltage) { SetVoltage(m_voltage, MaxSetVoltageTime, cancellationToken); } cancellationToken.ThrowIfCancellationRequested(); if (seq == 1) // init var on first cycle { ps.device = m_device; } bool x = InitSettings(seq, (uint)mparams.interval); // truncate for discrete integer result Stopwatch stopwatch = new Stopwatch(); TimeSpan duration = TimeSpan.FromSeconds((uint)mparams.interval); byte[] buffer = new byte[1024 * 1024]; // a5 5a 42 00 01 00 ae d5 44 56 b9 9b // flags: 0x0001 => spectrum is cleared and a new start time is set // start time: 0x5644d5ae => seconds since Dec 31, 1969, 16:00:00 GMT uint secondsSinceEpoch = (uint)(Math.Abs(Math.Round((DateTime.UtcNow - MCADevice.MCA527EpochTime).TotalSeconds))); MCAResponse response = m_device.Client.Send(MCACommand.Start(StartFlag.SpectrumClearedNewStartTime, false, false, false, secondsSinceEpoch)); if (response == null) { throw new MCADeviceLostConnectionException(); } const uint CommonMemoryBlockSize = 1440; // what's the most that could be left over from a previous attempt to decode? => 3 bytes byte[] rawBuffer = new byte[CommonMemoryBlockSize + 3]; ulong[] timestampsBuffer = new ulong[CommonMemoryBlockSize + 1]; uint commonMemoryReadIndex = 0; uint rawBufferOffset = 0; m_logger.TraceEvent(LogLevels.Verbose, 11901, "{0} start time for {1}", DateTime.Now.ToString(), seq); ulong accumulatedTime = 0, totalEvents = 0; int maxindex = 0; TimeSpan elapsed = new TimeSpan(0); stopwatch.Start(); while (true) { cancellationToken.ThrowIfCancellationRequested(); QueryState527ExResponse qs527er = (QueryState527ExResponse)m_device.Client.Send(MCACommand.QueryState527Ex()); if (qs527er == null) { throw new MCADeviceLostConnectionException(); } MCAState state = qs527er.MCAState; // pull off some data while we are waiting... uint commonMemoryFillLevel = qs527er.CommonMemoryFillLevel; uint bytesAvailable = commonMemoryFillLevel - commonMemoryReadIndex; elapsed = stopwatch.Elapsed; // snapshot if (state != MCAState.Run && bytesAvailable == 0) // requested time is complete { break; } if ((elapsed = stopwatch.Elapsed) > duration) // elapsed time is complete { break; } if (bytesAvailable >= CommonMemoryBlockSize) // a full data block { QueryCommonMemoryResponse qcmr = (QueryCommonMemoryResponse)m_device.Client.Send(MCACommand.QueryCommonMemory(commonMemoryReadIndex / 2)); if (qcmr == null) { throw new MCADeviceLostConnectionException(); } // bytesToCopy needs to always be even, so that commonMemoryReadIndex always stays even... uint bytesToCopy = Math.Min(bytesAvailable / 2, CommonMemoryBlockSize / 2) * 2; qcmr.CopyData(0, rawBuffer, (int)rawBufferOffset, (int)bytesToCopy); if (ps.writingFile && ps.file != null && ps.file.writer != null) // write the block { ps.file.WriteTimestampsRawDataChunk(rawBuffer, 0, (int)bytesToCopy); } rawBufferOffset += bytesToCopy; commonMemoryReadIndex += bytesToCopy; uint timestampsCount = m_device.TransformRawData(rawBuffer, ref rawBufferOffset, timestampsBuffer); // make sure rawBufferOffset is never greater than 3 after transforming data // => means something has gone wrong if (rawBufferOffset > 3) { throw new MCADeviceBadDataException(); } // copy the data out... if (timestampsCount > 0) { if (maxindex < RDT.State.maxValuesInBuffer) // accumulate { if (RDT.State.timeArray.Count < maxindex + timestampsCount) { RDT.State.timeArray.AddRange(new ulong[timestampsCount]); } for (int i = 0; i < timestampsCount; i++) { accumulatedTime += timestampsBuffer[i]; RDT.State.timeArray[maxindex] = accumulatedTime; maxindex++; // always 1 more than the last index } RDT.State.NumValuesParsed += timestampsCount; RDT.State.hitsPerChn[0] += timestampsCount; RDT.State.NumTotalsEncountered = RDT.State.NumValuesParsed; // only one channel } else // process { long _max = RDT.State.NumValuesParsed; if (_max < RDT.State.neutronEventArray.Count) // equalize the length of the empty neutron channel event list { RDT.State.neutronEventArray.RemoveRange((int)_max, RDT.State.neutronEventArray.Count - (int)_max); } else if (_max > RDT.State.neutronEventArray.Count) { RDT.State.neutronEventArray.AddRange(new uint[_max - RDT.State.neutronEventArray.Count]); } if (_max < RDT.State.timeArray.Count) { RDT.State.timeArray.RemoveRange((int)_max, RDT.State.timeArray.Count - (int)_max); } else if (_max > RDT.State.timeArray.Count) { RDT.State.timeArray.AddRange(new ulong[_max - RDT.State.timeArray.Count]); } m_logger.TraceEvent(LogLevels.Verbose, 88, "{0} {1} handling {2} timestampsCount {3} num", elapsed, duration, timestampsCount, RDT.State.NumValuesParsed); RDT.PassBufferToTheCounters((int)_max); maxindex = 0; totalEvents += RDT.State.NumTotalsEncountered; RDT.StartNewBuffer(); } } } else if (bytesAvailable > 0 && state != MCAState.Run) { // special case for when there's not a whole block left to read // we can only read up to the address: CommonMemorySize - 1440 uint commonMemorySize = qs527er.CommonMemorySize; uint readAddress = commonMemoryReadIndex; uint readOffset = 0; if (readAddress > commonMemorySize - 1440) { readOffset = readAddress - (commonMemorySize - 1440); readAddress -= readOffset; } QueryCommonMemoryResponse qcmr = (QueryCommonMemoryResponse)m_device.Client.Send(MCACommand.QueryCommonMemory(readAddress / 2)); if (qcmr == null) { throw new MCADeviceLostConnectionException(); } uint bytesToCopy = bytesAvailable; qcmr.CopyData((int)readOffset, rawBuffer, (int)rawBufferOffset, (int)bytesToCopy); if (ps.writingFile && ps.file != null && ps.file.writer != null) // write the block { ps.file.WriteTimestampsRawDataChunk(rawBuffer, (int)readOffset, (int)bytesToCopy); } rawBufferOffset += bytesToCopy; commonMemoryReadIndex += bytesToCopy; uint timestampsCount = m_device.TransformRawData(rawBuffer, ref rawBufferOffset, timestampsBuffer); //if (rawBufferOffset > 0) { // apparently this can happen. Perhaps when the device gets cut off (because of a timer event), right in the middle of writing? //throw new MCADeviceBadDataException(); // an Engineer from GBS said we are running on a very old firmware version, // perhaps that has something to do with it... //} if (timestampsCount > 0) { // copy the timestampsBuffer value into the RDT.State.timeArray, Q: wait to fill a much large internal buffer before calling the transform? if (maxindex < RDT.State.maxValuesInBuffer) // accumulate { if (RDT.State.timeArray.Count < maxindex + timestampsCount) { RDT.State.timeArray.AddRange(new ulong[timestampsCount]); } for (int i = 0; i < timestampsCount; i++) { accumulatedTime += timestampsBuffer[i]; RDT.State.timeArray[maxindex] = accumulatedTime; maxindex++; // always 1 more than the last index } RDT.State.NumValuesParsed += timestampsCount; RDT.State.hitsPerChn[0] += timestampsCount; RDT.State.NumTotalsEncountered = RDT.State.NumValuesParsed; // only one channel } else // process { long _max = RDT.State.NumValuesParsed; if (_max < RDT.State.neutronEventArray.Count) // equalize the length of the empty neutron channel event list { RDT.State.neutronEventArray.RemoveRange((int)_max, RDT.State.neutronEventArray.Count - (int)_max); } else if (_max > RDT.State.neutronEventArray.Count) { RDT.State.neutronEventArray.AddRange(new uint[_max - RDT.State.neutronEventArray.Count]); } if (_max < RDT.State.timeArray.Count) { RDT.State.timeArray.RemoveRange((int)_max, RDT.State.timeArray.Count - (int)_max); } else if (_max > RDT.State.timeArray.Count) { RDT.State.timeArray.AddRange(new ulong[_max - RDT.State.timeArray.Count]); } m_logger.TraceEvent(LogLevels.Verbose, 89, "{0} {1} handling {2} timestampsCount {3} num", elapsed, duration, timestampsCount, RDT.State.NumValuesParsed); RDT.PassBufferToTheCounters((int)_max); maxindex = 0; totalEvents += RDT.State.NumTotalsEncountered; RDT.StartNewBuffer(); } } } else { // give the device a break, not needed now because PassBufferToTheCounters processing takes time //Thread.Sleep(40); // 100? ms //m_logger.TraceEvent(LogLevels.Verbose, 99899, "{0} {1} handling {2} timestampsCount {3} num", elapsed, duration, 0, RDT.State.NumValuesParsed); } elapsed = stopwatch.Elapsed; // snapshot the time after the processing and before the next query if (maxindex > 0) // accumulated data was not completely processed above, so it happens here { long _max = RDT.State.NumValuesParsed; if (_max < RDT.State.neutronEventArray.Count) // equalize the length of the empty neutron channel event list { RDT.State.neutronEventArray.RemoveRange((int)_max, RDT.State.neutronEventArray.Count - (int)_max); } else if (_max > RDT.State.neutronEventArray.Count) { RDT.State.neutronEventArray.AddRange(new uint[_max - RDT.State.neutronEventArray.Count]); } if (_max < RDT.State.timeArray.Count) { RDT.State.timeArray.RemoveRange((int)_max, RDT.State.timeArray.Count - (int)_max); } else if (_max > RDT.State.timeArray.Count) { RDT.State.timeArray.AddRange(new ulong[_max - RDT.State.timeArray.Count]); } m_logger.TraceEvent(LogLevels.Verbose, 90, "{0} {1} handling {2} num", elapsed, duration, RDT.State.NumValuesParsed); RDT.PassBufferToTheCounters((int)_max); maxindex = 0; totalEvents += RDT.State.NumTotalsEncountered; RDT.StartNewBuffer(); } } // while time elapsed is less than requested time stopwatch.Stop(); m_logger.TraceEvent(LogLevels.Verbose, 11901, "{0} stop time for {1}", DateTime.Now.ToString(), seq); m_logger.TraceEvent(LogLevels.Info, 0, "MCA527[{0}]: Finished assay; read {1} events in {2}s for {3}", DeviceName, totalEvents, stopwatch.Elapsed.TotalSeconds, seq); m_logger.Flush(); RDT.Cycle.HighVoltage = m_device.GetHighVoltage(); if (ps.writingFile) { m_device.CreateWriteHeaderAndClose(ps.file); m_logger.TraceEvent(LogLevels.Verbose, 11921, "WriteHeader for {0}", seq); m_logger.Flush(); } lock (m_monitor) { m_cancellationTokenSource.Dispose(); m_cancellationTokenSource = null; } if (totalEvents == 0) // nothing to handle if no events, close up and continue { DAQControl.HandleEndOfCycleProcessing(this, new StreamStatusBlock(@"MCA527 Done")); m_logger.TraceEvent(LogLevels.Verbose, 11911, "HandleEndOfCycle for {0}", seq); m_logger.Flush(); } } catch (OperationCanceledException) { m_logger.TraceEvent(LogLevels.Warning, 767, "MCA527[{0}]: Stopping assay", DeviceName); m_logger.Flush(); DAQControl.StopActiveAssayImmediately(); throw; } catch (Exception ex) { m_logger.TraceEvent(LogLevels.Error, 0, "MCA527[{0}]: Error during assay: {1}", DeviceName, ex.Message); m_logger.TraceException(ex, true); m_logger.Flush(); DAQControl.HandleFatalGeneralError(this, ex); throw; } finally { } }
/// <summary> /// Set up timer callback and action event handlers for a DAQController instance, uses DAQBind subclass /// </summary> /// <returns></returns> public bool InitializeDAQController() { // DAQ daqbind = new DAQBind(); LMLoggers.LognLM applog = NC.App.Logger(LMLoggers.AppSection.App); daqbind.SetEventHandler(ActionEvents.EventType.PreAction, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.PreAction, applog, LogLevels.Verbose, o); daqbind.mProgressTracker.ReportProgress(0, s); //"..."); }); daqbind.SetEventHandler(ActionEvents.EventType.ActionPrep, (object o) => { string s = "Action Prep: "; //can also look at active instrument list here LogLevels.Verbose, s = s + Instr.Instruments.Active.Count + " devices found, [" + NC.App.Opstate.SOH + "] " + DateTimeOffset.Now.ToString("MMM dd yyy HH:mm:ss.ff K"); applog.TraceEvent(LogLevels.Verbose, 0xABCE, "Action Prep: SOH " + o.ToString() + "'"); daqbind.mProgressTracker.ReportProgress(0, s); //"Prep"); }); daqbind.SetEventHandler(ActionEvents.EventType.ActionStart, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionStart, applog, LogLevels.Verbose, o); daqbind.mProgressTracker.ReportProgress(1, s);//"Starting..."); }); daqbind.SetEventHandler(ActionEvents.EventType.ActionInProgress, (object o) => { measStatus = new MeasurementStatus(); measStatus.UpdateWithMeasurement(); measStatus.UpdateWithInstruments(); updateGUIWithChannelRatesData = true; string s2 = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionInProgress, applog, LogLevels.Verbose, o); if (!string.IsNullOrEmpty(s2)) { s2 = "(" + s2 + ")"; } int per = 0; try { switch (daqbind.CurAction) { case NCCAction.HVCalibration: if (measStatus.snaps.iss != null && measStatus.snaps.iss.hv != null) { per = Math.Abs(Math.Min(100, (int)Math.Round(100.0 * ((double)(measStatus.snaps.iss.hv.HVread - 1) / measStatus.snaps.iss.hv.HVsetpt)))); daqbind.mProgressTracker.ReportProgress(per, // a % est of steps string.Format("{0} volts, with max voltage {1} {2}", measStatus.snaps.iss.hv.HVread, measStatus.snaps.iss.hv.HVsetpt, s2)); } break; default: per = Math.Abs(Math.Min(100, (int)Math.Round(100.0 * ((double)(measStatus.CurrentRepetition - 1) / measStatus.RequestedRepetitions)))); daqbind.mProgressTracker.ReportProgress(per, // a % est of files string.Format("{0} of {1} {2}", measStatus.CurrentRepetition, measStatus.RequestedRepetitions, s2)); // dev note: need a better focused description of the state break; } } catch (ArgumentOutOfRangeException) { applog.TraceEvent(LogLevels.Verbose, 58, "{0} inconsistent", per); } }); daqbind.SetEventHandler(ActionEvents.EventType.ActionStop, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionStop, applog, LogLevels.Info, o); daqbind.mProgressTracker.ReportProgress(100, s); }); daqbind.SetEventHandler(ActionEvents.EventType.ActionCancel, (object o) => { string s = DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionCancel, applog, LogLevels.Warning, o); daqbind.mProgressTracker.ReportProgress(100, s); }); daqbind.SetEventHandler(ActionEvents.EventType.ActionFinished, (object o) => { string s = ""; if (o != null && o is DAQControl) { DAQControl f = (DAQControl)o; measStatus = new MeasurementStatus(); // preps local state for refresh on channel and computed rates for now, follow up with all other state measStatus.UpdateWithMeasurement(); s = DAQControl.MeasStatusString(measStatus); updateGUIWithChannelRatesData = true; UpdateGUIWithNewdata(); } else { s = "Finished: SOH " + NC.App.Opstate.SOH + " but no processing occurred " + DAQControl.LogAndSkimDAQProcessingStatus(ActionEvents.EventType.ActionFinished, applog, LogLevels.Verbose, o); } NC.App.Opstate.SOH = OperatingState.Stopped; // in case we got here after a Cancel // general logger: to the console, and/or listbox and/or log file or DB daqbind.mProgressTracker.ReportProgress(100, s); applog.TraceEvent(LogLevels.Verbose, ActionEvents.logid[ActionEvents.EventType.ActionFinished], s); // specialized updater for UI or file daqbind.mProgressTracker.ReportProgress(100, s); }); return(true); }