private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Spectrometer spectrometer = e.Result as Spectrometer; if (spectrometer == currentSpectrometer) { updateStartButton(false); } // should we auto-exit? if (opts.autoStart && opts.scanCount > 0) { bool shutdown = true; lock (spectrometers) { foreach (Spectrometer s in spectrometers) { SpectrometerState ss = spectrometerStates[s]; if (ss.scanCount < opts.scanCount) { shutdown = false; } } } if (shutdown) { Close(); } } }
//////////////////////////////////////////////////////////////////////// // Business Logic //////////////////////////////////////////////////////////////////////// void initializeSpectrometer(Spectrometer s) { SpectrometerState state = new SpectrometerState(s, opts); // TODO: move into SpectrometerState ctor state.worker.DoWork += backgroundWorker_DoWork; state.worker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; spectrometerStates.Add(s, state); chart1.Series.Add(state.series); if (!s.isARM) { s.triggerSource = TRIGGER_SOURCE.INTERNAL; } s.integrationTimeMS = s.eeprom.minIntegrationTimeMS; numericUpDownIntegTimeMS.Value = s.eeprom.minIntegrationTimeMS; numericUpDownIntegTimeMS.Minimum = s.eeprom.minIntegrationTimeMS; // numericUpDownIntegTimeMS.Maximum = s.eeprom.maxIntegrationTimeMS; // disabled to allow long integration times if (s.pixels > 0) { logger.info("Found {0} {1} with {2} pixels from {3:f2} to {4:f2}nm", s.model, s.serialNumber, s.pixels, s.wavelengths[0], s.wavelengths[s.wavelengths.Length - 1]); } else { logger.error("Found [model: {0}] [serial: {1}] with {2} pixels", s.model, s.serialNumber, s.pixels); } // default to high-resolution laser power s.laserPowerResolution = Spectrometer.LaserPowerResolution.LASER_POWER_RESOLUTION_1000; }
//////////////////////////////////////////////////////////////////////// // BackgroundWorker: GUI Updates //////////////////////////////////////////////////////////////////////// /// <summary> /// Update the graph at 10Hz regardless of integration time(s) /// (prevents CPU overruns). /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void backgroundWorkerGUIUpdate_DoWork(object sender, DoWorkEventArgs e) { logger.debug("GUIUpdate thread starting"); BackgroundWorker worker = sender as BackgroundWorker; ushort lowFreqOperations = 0; while (true) { Thread.Sleep(100); if (worker.CancellationPending || shutdownPending) { break; } chart1.BeginInvoke(new MethodInvoker(delegate { updateGraph(); })); // once a second, update temperatures if (lowFreqOperations++ > 10) { foreach (Spectrometer s in spectrometers) { if (!s.isARM) { SpectrometerState state = spectrometerStates[s]; state.detTempDegC = s.detectorTemperatureDegC; } } lowFreqOperations = 0; } } logger.debug("GUIUpdate thread exiting"); }
private void checkBoxTakeDark_CheckedChanged(object sender, EventArgs e) { SpectrometerState state = spectrometerStates[currentSpectrometer]; lock (spectrometers) { if (checkBoxTakeDark.Checked) { if (state.spectrum != null) { currentSpectrometer.dark = state.spectrum; state.spectrum = new double[currentSpectrometer.pixels]; checkBoxTakeReference.Enabled = true; } } else { currentSpectrometer.dark = null; // if we have no dark, then we can have no reference state.reference = null; checkBoxTakeReference.Checked = checkBoxTakeReference.Enabled = false; // if we have no dark, we can do no processing radioButtonModeScope.Checked = true; } } }
void updateGraph() { lock (spectrometers) { foreach (Spectrometer spectrometer in spectrometers) { SpectrometerState state = spectrometerStates[spectrometer]; if (state.spectrum == null) { // logger.debug("not graphing because spectrum null"); continue; } Series series = state.series; series.Points.Clear(); if (graphWavenumbers && spectrometer.wavenumbers != null) { for (uint i = 0; i < spectrometer.pixels; i++) { series.Points.AddXY(spectrometer.wavenumbers[i], state.spectrum[i]); } } else { for (uint i = 0; i < spectrometer.pixels; i++) { series.Points.AddXY(spectrometer.wavelengths[i], state.spectrum[i]); } } // extra handling for current spectrometer if (spectrometer == currentSpectrometer) { /// has spectra, so allow darks and traces checkBoxTakeDark.Enabled = buttonAddTrace.Enabled = buttonSave.Enabled = true; labelDetTempDegC.Text = String.Format("{0:f1}°C", state.detTempDegC); } } chart1.ChartAreas[0].AxisY.IsStartedFromZero = false; chart1.ChartAreas[0].RecalculateAxesScale(); } }
private void buttonStart_Click(object sender, EventArgs e) { SpectrometerState state = spectrometerStates[currentSpectrometer]; if (!state.running) { logger.info("Starting acquisition"); updateStartButton(true); state.worker.RunWorkerAsync(currentSpectrometer); } else { logger.info("Stopping acquisition"); state.worker.CancelAsync(); } }
//////////////////////////////////////////////////////////////////////// // Background Worker: Acquisition Threads //////////////////////////////////////////////////////////////////////// /// <summary> /// Perform all acquisitions in background threads so the GUI stays responsive. /// </summary> /// <remarks> /// Note that this method is used by potentially several different /// BackgroundWorkers in parallel (one per attached spectrometer). /// /// TODO: rename backgroundWorkerAcquisition /// </remarks> private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { Spectrometer spectrometer = (Spectrometer)e.Argument; SpectrometerState state = spectrometerStates[spectrometer]; string prefix = String.Format("Worker.{0}.{1}", spectrometer.model, spectrometer.serialNumber); state.running = true; BackgroundWorker worker = sender as BackgroundWorker; while (true) { // end thread if we've been asked to cancel if (worker.CancellationPending) { break; } // logger.debug("workerAcquisition: getting spectrum"); double[] raw = spectrometer.getSpectrum(); if (raw == null) { Thread.Sleep(100); continue; } // process for graphing lock (spectrometers) state.processSpectrum(raw); // end thread if we've completed our allocated acquisitions if (opts.scanCount > 0 && state.scanCount >= opts.scanCount) { break; } int delayMS = (int)Math.Max(100, opts.scanIntervalSec * 1000); if (delayMS > 0) { Thread.Sleep(delayMS); } } state.running = false; e.Result = spectrometer; // pass spectrometer handle to _Completed callback }
private void buttonAddTrace_Click(object sender, EventArgs e) { SpectrometerState state = spectrometerStates[currentSpectrometer]; Series trace = new Series(); trace.IsVisibleInLegend = false; trace.ChartType = SeriesChartType.Line; foreach (DataPoint p in state.series.Points) { trace.Points.Add(p); } traces.Add(trace); chart1.Series.Add(trace); buttonClearTraces.Enabled = true; }
private void buttonSave_Click(object sender, EventArgs e) { if (opts.saveDir.Length == 0) { if (!initSaveDir()) { return; } } // More complex implementations could save all spectra from all spectrometers; // or include snapped traces; or export directly to multi-tab Excel spreadsheets. lock (spectrometers) { SpectrometerState state = spectrometerStates[currentSpectrometer]; if (state.spectrum == null) { return; } state.save(); } }
private void checkBoxTakeReference_CheckedChanged(object sender, EventArgs e) { SpectrometerState state = spectrometerStates[currentSpectrometer]; lock (spectrometers) { bool success = false; if (checkBoxTakeReference.Checked) { if (state.spectrum != null) { // business logic should ensure this, but just in case if (currentSpectrometer.dark != null) { state.reference = new double[state.spectrum.Length]; Array.Copy(state.spectrum, state.reference, state.spectrum.Length); success = true; radioButtonModeTransmission.Enabled = true; radioButtonModeAbsorbance.Enabled = true; } else { logger.error("Can't take reference without dark"); } } } if (!success) { state.reference = null; radioButtonModeTransmission.Enabled = false; radioButtonModeAbsorbance.Enabled = false; } } }
void updateCurrentSpectrometer() { if (currentSpectrometer == null) { groupBoxSettings.Enabled = groupBoxControl.Enabled = toolStripMenuItemTest.Enabled = false; return; } SpectrometerState state = spectrometerStates[currentSpectrometer]; // update tree view // if (!state.spectrometer.isARM()) treeViewSettings_DoubleClick(null, null); // update start button updateStartButton(spectrometerStates[currentSpectrometer].running); // update basic controls numericUpDownIntegTimeMS.Value = currentSpectrometer.integrationTimeMS; numericUpDownBoxcarHalfWidth.Value = currentSpectrometer.boxcarHalfWidth; numericUpDownScanAveraging.Value = currentSpectrometer.scanAveraging; // update TEC controls numericUpDownDetectorSetpointDegC.Minimum = (int)currentSpectrometer.eeprom.detectorTempMin; numericUpDownDetectorSetpointDegC.Maximum = (int)currentSpectrometer.eeprom.detectorTempMax; numericUpDownDetectorSetpointDegC.Enabled = currentSpectrometer.eeprom.hasCooling; // update laser controls if (currentSpectrometer.hasLaser) { numericUpDownLaserPowerPerc.Enabled = checkBoxLaserEnable.Enabled = true; checkBoxLaserEnable.Checked = currentSpectrometer.laserEnabled; } else { numericUpDownLaserPowerPerc.Enabled = checkBoxLaserEnable.Enabled = checkBoxLaserEnable.Checked = false; } checkBoxTakeDark.Enabled = buttonSave.Enabled = state.spectrum != null; checkBoxTakeReference.Enabled = state.spectrum != null && currentSpectrometer.dark != null; if (state.processingMode == SpectrometerState.ProcessingModes.SCOPE) { radioButtonModeScope.Checked = true; } else if (state.processingMode == SpectrometerState.ProcessingModes.TRANSMISSION) { radioButtonModeTransmission.Checked = true; } else { radioButtonModeAbsorbance.Checked = true; } groupBoxSettings.Enabled = groupBoxControl.Enabled = toolStripMenuItemTest.Enabled = labelDetTempDegC.Visible = true; }